书接上文,继续编写链表的功能
4.链表的中间插入
在链表中,本身是没有下标这样的概念的,不像顺序表,顺序表根据下标访问元素,O(1)复杂度。链表需要遍历之后找到正确的位置才能进行插入,为O(N)复杂度。
但是这件事在JAVA中是个例外,JAVA标准库LinkedList引入了下标概念。
public int size(){int size =0;for(Node cur = head; cur.next!=null;cur = cur.next){size++;}return size;}//4.中间插入的操作public void add(int index,String value){int size = size();//1.首先判断index是否合法if(index<0 || index >size){throw new RuntimeException("下标超出范围");}//1.首先要找到index-1的prev节点Node prev = head;//上述代码出现了一个问题:如果是头插,那么就会导致循环无法进去,那么prev是第一个节点,插入的永远是下标为1的地方//*特殊情况需要特殊考虑if(index ==0){addFirst(value);return;}for (int i = 0; i < index-1; i++) {prev = prev.next;}//3.此时进行修改操作Node newNode = new Node(value);newNode.next = prev. next;prev.next = newNode;}
//5.看某个元素是否被包含在链表当中
public boolean contains(String value){for(Node cur = head;cur.next != null;cur = cur.next){if(cur.value.equals(value)){return true;}}return false;}
//6.找到了就返回index
public int indexOf(String value){int index =0;for(Node cur = head;cur.next != null;cur = cur.next){if(cur.value.equals(value)){return index;}else {index++;}}return -1;}
//7.根据下标删除
//7.根据下标删除public void remove( int index){int size = size();//1.首先要判断index的值是否合法if( index <0 || index >=size){throw new RuntimeException("下标越界");}//*要考虑特殊的删除头节点if(index ==0){head = head.next;return ;}//2.其次要找到上一个节点Node prev = head;for (int i = 0; i < index-1; i++) {prev = prev.next;}//3.然后要进行删除操作prev.next = prev.next.next;}
//8.根据值来删除
public void remove(String value){//还要考虑空链表的情况if(head == null){return;}//有关添加删除操作都要考虑前一个节点 所以每次创建的都是prevNode prev = head;for(;prev!=null;prev= prev.next){if(prev.next!= null&&prev.next.equals(value)){//找到了break;}}//出来之后有两种情况 一种是找到了value 另一种是遍历完了都没有找到valueif(prev == null){return;}else{//找到了,进行删除操作Node cur = prev.next;prev.next = cur.next;}}
//9.全部删除的clear操作
//9.clear()public void clear(){head = null;}
至此,LinkedList基本写成了
总体:
package LinkedList;//要想实现链表的基本功能,首先要表示链表的一个节点
//对于节点这个类来说,他的本身功能单一,比较简单
//如果高一些get set 方法 ,后续代码就会显得很难看class Node{public String value;//节点保存的值public Node next;//这个节点的下一个元素public Node(String value) {this.value = value;this.next = null;}//当我们创建一个Node的时候,就创建好了链表的头节点,此时链表头节点的值可以确定,且尚未含有下一个节点
}
//这是一个单链表的节点 双向链表还需要一个prev
public class MyLinkedList {//把链表的头节点表示出来,此时整个链表就都能被获取到了//此处不包含傀儡系欸但,head== null 的时候表示空的链表private Node head = null;//1.链表的头插操作public void addFirst(String value){Node newNode = new Node(value);newNode.next =head;head = newNode;//head只是一个引用类型!!!}//2.遍历链表的操作public String toString(){StringBuilder stringBuilder = new StringBuilder();stringBuilder.append("[");for (Node cur = head;cur!= null; cur= cur.next) {stringBuilder.append(cur.value);if(cur.next!= null) {stringBuilder.append(",");}}stringBuilder.append("]");return stringBuilder.toString();}//3.尾插操作public void addLast( String value){//1.首先找到最后一个节点Node tail = head;//*还要考虑特殊的情况if(head == null){Node node = new Node(value);head = node;return;}for (;tail.next!=null; tail = tail.next) {if(tail.next==null){break;}}//此时的tail也就是我们要找的最后一个节点Node node = new Node(value);tail.next = node;node.next = null;}public int size(){int size =0;for(Node cur = head; cur.next!=null;cur = cur.next){size++;}return size;}//4.中间插入的操作public void add(int index,String value){int size = size();//1.首先判断index是否合法if(index<0 || index >size){throw new RuntimeException("下标超出范围");}//1.首先要找到index-1的prev节点Node prev = head;//上述代码出现了一个问题:如果是头插,那么就会导致循环无法进去,那么prev是第一个节点,插入的永远是下标为1的地方//*特殊情况需要特殊考虑if(index ==0){addFirst(value);return;}for (int i = 0; i < index-1; i++) {prev = prev.next;}//3.此时进行修改操作Node newNode = new Node(value);newNode.next = prev. next;prev.next = newNode;}//5.看某个元素是否被包含在链表当中public boolean contains(String value){for(Node cur = head;cur != null;cur = cur.next){if(cur.value.equals(value)){return true;}}return false;}//6.找到了就返回indexpublic int indexOf(String value){int index =0;for(Node cur = head;cur != null;cur = cur.next){if(cur.value.equals(value)){return index;}else {index++;}}return -1;}//7.根据下标删除public void remove( int index){int size = size();//1.首先要判断index的值是否合法if( index <0 || index >=size){throw new RuntimeException("下标越界");}//*要考虑特殊的删除头节点if(index ==0){head = head.next;return ;}//2.其次要找到上一个节点Node prev = head;for (int i = 0; i < index-1; i++) {prev = prev.next;}//3.然后要进行删除操作prev.next = prev.next.next;}//8.根据值来删除public void remove(String value){//还要考虑空链表的情况if(head == null){return;}//有关添加删除操作都要考虑前一个节点 所以每次创建的都是prevNode prev = head;for(;prev!=null;prev= prev.next){if(prev.next!= null&&prev.next.equals(value)){//找到了break;}}//出来之后有两种情况 一种是找到了value 另一种是遍历完了都没有找到valueif(prev == null){return;}else{//找到了,进行删除操作Node cur = prev.next;prev.next = cur.next;}}//9.clear()public void clear(){head = null;}
}
class Solution {public ListNode removeElements(ListNode head, int val) {//1.首先判断链表是否为空if(head == null){return null;}//2.利用循环删除每一个值为val的元素,但是是从head后面开始删除的!ListNode prev = head;ListNode cur = prev.next;while(cur!=null){if(cur.val == val){//就触发删除操作prev.next = cur.next;//还要将cur进行后置cur = prev.next;}else{prev = cur;cur =cur.next;}}//cur == null 再判定开头的元素if(head.val==val){head = head.next;}return head;}}
1.这道题的要点就在于头节点的删除,如果head=[7,7,7,7],我们就先不管头节点,先把后面值等于val的节点删除,然后循环出来再去考虑头节点。
现在要得到这个链表的翻转链表
思路:分别设置三个引用变量:prev,cur,next。使三者遍历整个链表,按照如下图所示的操作完成链表的翻转。
*为什么一定要设置三个,而不能只使用cur?
因为在完成翻转操作之后,我们还想让循环继续,但是此时cur.next=prev,所以我们要通过next这个引用变量为我们指明前方的道路
也就是说,pev=cur;cur = next; next = next.next;
一直到cur == null,然后跳出循环
class Solution {public ListNode reverseList(ListNode head) {//1.首先判断链表是否为空if(head == null){return null;} //2.如果链表里只含有一个元素,那么翻转还是不反转没有任何影响if(head.next == null){return head;}//3.来处理一般情况//首先要创建三个引用变量ListNode prev = null;ListNode cur = head;ListNode next = cur.next;//再创建一个新的头节点,待会儿返回ListNode newHead = null;while(cur!=null){next = cur.next;if(next == null){//已经全部完成了newHead = cur;//这个地方不能break,因为下面的操作还需要更新!!!}cur.next = prev;prev = cur;cur = next;}
思路:首先我们可以计算处链表的长度,其次只要将链表的长度/2 之后再遍历我们就可以得到第二个中间节点的值了
class Solution {public ListNode middleNode(ListNode head) {int size = 0;for(ListNode cur = head ; cur!= null;cur = cur.next){size++;}int num = size/2;ListNode cur = head;while(num != 0){num--;cur = cur.next;}return cur;}
}
思路:1.创建新的链表表示最终结构
2.搞两个引用,分别指向两个链表
3.比较这两个引用的值,谁小,就把哪个节点取出来,插入到新链表的末尾,如果这两个引用,有任何一个指向了null,说明该链表就结束了,就把另一个链表剩余的元素都添加到新链表的末尾即可。
代码:
class Solution {public ListNode mergeTwoLists(ListNode list1, ListNode list2) {//1.判断list1为空链表的情况if(list1 == null){return list2;}//2。判断list2为空链表的情况if(list2 == null){return list1;}//3.考虑一般情况//为了使后续的插入方便我们首先创建一个傀儡节点以及末尾节点ListNode newHead = new ListNode(0);ListNode tail = newHead;ListNode cur1 = list1;ListNode cur2 = list2;while(cur1!=null && cur2 !=null){if(cur1.val < cur2.val){//cur1比较小 所以把cur1放进来ListNode newNode =cur1;tail.next = newNode;tail = newNode; cur1 = cur1.next;}else{ListNode newNode =cur2;tail.next = newNode;tail = newNode; cur2 = cur2.next;}}//出来的时候,要么cur1还有剩余,要么cur2还有剩余if(cur1 != null){//把剩余的链表给他接上去tail.next = cur1;}if(cur2 != null){tail.next = cur2;}return newHead.next;//不能返回newHead 要返回傀儡节点的下面一个节点}
}
写不动了!明天再见!