書接上文,繼續編寫鏈表的功能
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 要返回傀儡節點的下面一個節點}
}
寫不動了!明天再見!