java Node类型和string java里node_leetcode专栏简介 :java语法及数据结构

java Node类型和string java里node_算法_02题目来源:leetcode,牛客,剑指offer

java Node类型和string java里node_链表_03创作目标:从java语法角度实现底层相关数据结构,达到手撕各类题目的水平.

java Node类型和string java里node_java Node类型和string_04希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长.

java Node类型和string java里node_leetcode_05学历代表过去,能力代表现在,学习能力代表未来!

目录

前言

一.线性表

二.顺序表

2.1顺序表简介:

2.2接口实现增删改查:

1)基本类的搭建:

 2)基本操作方法:

2.3顺序表总结:

三.链表:

3.1链表的结构及其基本概念.

3.2 链表操作的具体实现:

1)基本类的搭建:

2)链表基本操作方法的实现:

总结



前言

顺序表和链表的快速入门,旨在掌握顺序表和链表的底层构造方法,以达到熟练掌握并能在leetcode独立刷题为主要目的.希望我的内容能给大家带来帮助!


一.线性表

        线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...

        线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

java Node类型和string java里node_算法_06

二.顺序表

2.1顺序表简介:

物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

顺序表一般可以分为:

  • 静态顺序表:使用定长数组存储。
  • 动态顺序表:使用动态开辟的数组存储。
  • 静态顺序表适用于确定知道需要存多少数据的场景.
  • 静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用.
  • 相比之下动态顺序表更灵活, 根据需要动态的分配空间大小.

2.2接口实现增删改查:

1)基本类的搭建:

        明白顺序表的定义之后,通过实现顺序表的所有功能来从底层了解顺序表,就目前来看这的确是学习数据结构最有效的不二法门.在实现各种底层方法时我们先搭建好顺序表所需的所有类和方法.

       首先我们构造一个My_ArrayList{}类,并在类中阐明顺序表所需的基本单元,也就是元素个数usedSize和顺序表长度int[]elem.最后我们定义一个构造方法用来初始化顺序表长度即可,这里大家可能不太理解usedSize的作用,现在顺序表为空,如果我往顺序表放入一个元素,那么usedSize就会++.

public class My_ArrayList { public int[]elem; public int usedSize; public My_ArrayList() { this.elem = new int[10]; } }

        搭建好顺序表的基本类后我们就可以逐步实现其操作方法了 !

 2)基本操作方法:

1.打印顺序表:

这时usedSize的作用就体现出来了,usedSize为多少我们就打印几个元素.

// 打印顺序表 public void display() { for (int i = 0; i < usedSize; i++) { System.out.println(this.elem[i] + ""); } }

2.判断输入位置是否合法?

//判断pos位置是否合法 public boolean legal_Pos(int pos){ if (pos < 0 || pos > usedSize) { System.out.println("输入位置不合法"); return true; }else return false; }

 3.判断顺序表是否为空?

//判断是否为空 public boolean isEmpty(){ if (usedSize==0) { System.out.println("元素为空"); return true; }else { return false; } }

4.判断顺序表是否满了?

boolean isFull(){ return this.elem.length==this.usedSize; }

 5.获取pos位置元素

// 获取 pos 位置的元素 public int getPos(int pos) { if (legal_Pos(pos)){ return -1; } return elem[pos]; }

6. 查找某个元素对应位置

// 查找某个元素对应的位置 public int search(int toFind) { for (int i = 0; i < usedSize; i++) { if (toFind==this.elem[i]){ return i; } } return -1; }

7.判断是否包含某个元素?(删除时用到)

// 判定是否包含某个元素 public boolean contains(int toFind) { for (int i = 0; i < usedSize; i++) { if(elem[i]==toFind){ return true; } } return false; }

完成以上方法后我们就可实现其具体内容了!

1.增加元素

        先判断输入位置的合法性,再判断顺序表是否满了?如果满了需要扩容这里我们采用二倍扩容.如果没有满我们先将pos位置以及pos位置之后的元素统一向后挪动一个单位,这时pos位置空出,我们再将pos位置赋值为我们想要的元素,一定不要忘记usedSize++.

public void add(int pos, int data) { if(legal_Pos(pos)){ return ; } if (isFull()) { this.elem = Arrays.copyOf(this.elem,2*this.elem.length); } for (int i = usedSize - 1; i >= pos; i++) { elem[i] = elem[i - 1]; } elem[pos]=data; usedSize++; }

2.删除元素

        先判断顺序表是否为空,再判断要删除的元素是否存在,如果存在记录其下标.再将其下标之后的元素统一向前挪动一个单位,这样就可以将其覆盖从而达到删除的效果了.如果想要删除全部的元素只需加个循环语句.

//删除第一次出现的关键字key public void remove(int toRemove) { int key = 0; if(isEmpty()){ return; } if(contains(toRemove)){ key = search(toRemove); }else { System.out.println("没有你要找的元素"); } for (int i = key; i <usedSize-1 ; i++) { elem[i] = elem[i+1]; } usedSize--; }

3. 清空顺序表

        由于顺序表中的元素都是数据类型,不包含复杂的引用所以我们清空的方式可以暴力一些.

// 清空顺序表 public void clear() { usedSize=0; }

2.3顺序表总结:

  •  顺序表中间/头部的插入删除,时间复杂度为O(N)
  •  扩容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  • 扩容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

既然存在以上问题那就说明顺序表虽然利于查询,但还是有着不足之处,而这部分法不足之处就靠链表来完善了.


三.链表:

3.1链表的结构及其基本概念.

         链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。链表的头结点极其重要,因为整个链表通常情况下靠头结点来维护.

java Node类型和string java里node_java Node类型和string_07

java Node类型和string java里node_java Node类型和string_08

实际情况中链表多种多样,以下情况组合起来就有8种链表结构:

1.单项 双向

2.带头 不带头

3.循环 非循环 

虽然有这么多的链表结构但我们重点掌握2种,为什么?因为这两种最难,面试笔试最常考.

1.无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多.

2.无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表 . 


Tips:无头不代表没有头结点,只是没有一个虚拟的头结点,由下图我们可以看出其中区别

java Node类型和string java里node_链表_09


3.2 链表操作的具体实现:

1)基本类的搭建:

        链表不同于顺序表,它在内存中的分布并不是连续的.基于这一特性,我们需专门构造一个节点类和一个链表类,节点类用于存放节点的属性如:数据域和指针域.而链表类用于将各个节点连接,并定义其各种操作方法.

数据域,指针域并构造初始化数据的方法.接着在链表类中定义一个头结点,然后构建一个方法用一种较为粗暴的方式枚举出链表.当然后续我们会改进链表的创建方法.构建好这些后,我们创建一个测试类,最简单的链表就实现了.

class ListNode{ public int val; public ListNode next; public ListNode(int val){ this.val = val; } } public class My_LinkedList { public ListNode head; public void createList() { ListNode listNode1 = new ListNode(12); ListNode listNode2 = new ListNode(23); ListNode listNode3 = new ListNode(34); ListNode listNode4 = new ListNode(45); ListNode listNode5 = new ListNode(56); ListNode listNode6 = new ListNode(23); listNode1.next = listNode2; listNode2.next = listNode3; listNode3.next = listNode4; listNode4.next = listNode5; listNode5.next = listNode6; this.head = listNode1; } }public class demo3 { public static void main(String[] args) { My_LinkedList my_linkedList = new My_LinkedList(); my_linkedList.createList(); }

2)链表基本操作方法的实现:

1.打印链表:

        因为链表的头不能动,链表靠头结点来维护,所以我们创建一个节点,遍历一遍链表即可,不过要注意链表没有++的概念,想要后移必须 cur = cur.next;

//打印链表 public void display() { ListNode cur = head; while (cur != null) { System.out.print(cur.val + " "); cur = cur.next; } }

2.获取链表的长度:

        思想与打印链表一致,不过是加了一个加法器count.

//得到单链表的长度 public int size() { ListNode cur = head; int count = 0; while (cur != null) { count++; cur = cur.next; } return count; }

 3.返回Index位置的元素:

        假设链表长度为4,打印第3位的元素,那么cur只需走两步即可.

java Node类型和string java里node_算法_10

//返回Index位置的元素 public ListNode FindIndex(int index) { ListNode cur = head; while (index - 1 != 0) { cur = cur.next; } return cur; }

4.查找关键字key是否在链表中:

//查找是否包含关键字key是否在单链表当中 public boolean contains(int key) { ListNode cur = head; while (cur != null) { if (cur.val == key) { return true; } cur = cur.next; } return false; }

Tips:以上都是辅助操作,接下来才是链表的真正实用功能!

1.头部插入:

        创建一个节点让其指向head,然后把head前移一位即可.

java Node类型和string java里node_算法_11

//头插法 public void addFirst(int data) { ListNode node = new ListNode(data); node.next = head; head = node; }

2.尾部插入:

        创建一个节点,判断链表是否为空如果为空直接等于头结点即可.如果不为空再定义一个ListNode类型的变量cur,让cur遍历一遍链表找到最后一位,然后把最后一位指向node即可.这里大家不必纠结为什么node最后没有置为空,创建结点类时指针域没有赋值,默认为null.

java Node类型和string java里node_顺序表_12


//尾插法 public void addLast(int data) { ListNode node = new ListNode(data); if (head == null) { head = node; } else { ListNode cur = head; while (cur.next != null) { cur = cur.next; } cur.next = node; } }

3.任意位置插入:

头插法即可,如果插入最后一位调用尾插即可.如果上述三种情况都不符合调用我们之前写好的返回index位置的元素的方法,接收到要插入的位置,然后创建一个节点node插入即可.(cur指向的位置变为node指向,再让cur指向node)

java Node类型和string java里node_算法_13

//任意位置插入,第一个数据节点为0号下标 public void addIndex(int index, int data) { if (index < 0 || index > size()) { System.out.println("输入位置不合法 "); return; } if (index == 0) { addFirst(data); return; } if (index == size()) { addLast(data); return; } ListNode cur = FindIndex(index); ListNode node = new ListNode(data); node.next = cur.next; cur.next = node; return; }

 4.找到前驱,为删除操作做准备:

        当我们要删除一个元素时,一定要知道它前面的元素.也就是我们常说的前驱

//找到前驱 public ListNode searchPrev(int key) { ListNode cur = head; while (cur.next != null) { if (cur.next.val == key) { return cur; } cur = cur.next; } return null; }

5.删除第一次出现的关键字为key的节点:

        首先单链表不能为空,如果没有意义了,其次如果要删除head节点,直接把head向后移一位即可.如果不是以上两种情况,那么我们调用之前写过的找前驱的函数,找到要删除节点的前驱,(如果返回的前驱为0,说明输入的的节点不存在)然del指向的位置变成cur指向即可.

java Node类型和string java里node_链表_14


//删除第一次出现关键字为key的节点 public void remove(int key) { if (head == null) { System.out.println("单链表为空不能删除!"); return; } if (head.val == key) {//删除头结点 head = head.next; return; } ListNode cur = searchPrev(key); if (cur == null) { System.out.println("删除的节点不存在"); return; } ListNode del = cur.next; cur.next = del.next; }

6.删除所有关键字为key的节点:(力扣原题)

        首先还是判断链表是否为空,这时如果要删头结点我们不能直接删,因为头结点后一个也可能要删除,所以头结点我们留到最后删.其次我们定义一个前驱prev和一个cur,假设我们只定义一个cur,一但遇到多个需要删的元素挨在一起根本就无从下手.所以我们将prev=head,cur = head.next.此时prev就像个掌握生杀大权的长官,而cur就像一个士兵一样不停的探路.当cur!=key时说明安全,prev就向后走一位.当cur==key时说明危险,prev就不走了直接指向cur的下一位.

java Node类型和string java里node_链表_15


//删除所有值为key的节点!!上升到面试笔试的难度 public ListNode removeAllKey(int key) { if (head == null) { System.out.println("单链表不能为空!"); return null; } ListNode prev = head; ListNode cur = head.next; while (cur != null) { if (cur.val == key) { prev.next = cur.next; cur = cur.next; } else { prev = cur; cur = cur.next; } } if (head.val == key) { head = head.next; } return this.head; }

java Node类型和string java里node_链表_16


7.清空链表:

        如果我们直接拿head置空,会发现无法向后走,所以定义curNode记录head的后一位让其向后走.

public void clear() { while (head != null) { ListNode curNext = head.next; head.next = null; head = curNext; } }


总结


        以上就是快速入门顺序表和链表的全部内容了,希望我通俗易通的讲解能够帮到你!后续会推出链表的面试笔试题已经双向链表,敬请期待!