链表

链表 : 跳转结构

一 . 单链表

单链表: 值,一条next指针

  1. public static class Node {
  2. public int value;
  3. public Node next;
  4. public Node(int data) {
  5. value = data;
  6. }
  7. }

二. 双链表

双链表: 值,一条last指针,一条next指针

  1. public static class DoubleNode {
  2. public int value;
  3. public DoubleNode last;
  4. public DoubleNode next;
  5. public DoubleNode(int data) {
  6. value = data;
  7. }
  8. }

三. 反转链表

(一)单链表

思路

04. 链表 - 图1

04. 链表 - 图2

  1. 需要一个 后指针cur指向head 需要一个前指针pre指向null temp记录环境(初始为null)
  2. 如果head位置有值
  3. head.next位置保存到temp里面
  4. head.next指向pre
  5. pre指向head
  6. head指向temp
  7. 循环2~6

链表结构代码

  1. public static class Node {
  2. public int value;
  3. public Node next;
  4. public Node(int data) {
  5. value = data;
  6. }
  7. }

反转实现代码

  1. public static Node reverseLinkedList(Node head) {
  2. Node pre = null;
  3. Node next = null;
  4. while (head != null) {
  5. next = head.next;
  6. head.next = pre;
  7. pre = head;
  8. head = next;
  9. }
  10. return pre;
  11. }

(二)双链表

思路

  1. 需要一个 后指针cur指向null 需要一个前指针pre指向null temp记录环境(初始为null)
  2. 如果 head位置有值
  3. headnext 指针保存到 temp (当前为null)
  4. headlast 指针指向 next
  5. pre指向head
  6. head指向temp
  7. 循环2~6

链表结构代码

  1. public static class DoubleNode {
  2. public int value;
  3. public DoubleNode last;
  4. public DoubleNode next;
  5. public DoubleNode(int data) {
  6. value = data;
  7. }
  8. }

反转实现代码

  1. public static Node reverseLinkedList(Node head) {
  2. Node pre = null;
  3. Node next = null;
  4. while (head != null) {
  5. next = head.next;
  6. head.next = pre;
  7. pre = head;
  8. head = next;
  9. }
  10. return pre;
  11. }

剑指 Offer 24. 反转链表

  1. // head
  2. // a -> b -> c -> null
  3. // c -> b -> a -> null
  4. public static Node reverseLinkedList(Node head) {
  5. Node pre = null;
  6. Node next = null;
  7. while (head != null) {
  8. next = head.next; //用next记录下一个节点
  9. head.next = pre;
  10. pre = head;
  11. head = next;
  12. }
  13. return pre;
  14. }

四. 单链表简单操作

  1. public class Mylist {
  2. public Node head; //定义一个头结点
  3. public Node current; //当前结点
  4. //添加元素
  5. public void add(int data){
  6. //判断链表是否为空
  7. if(head==null) //如果头结点为空,说明这个链表还没有被创建
  8. {
  9. head = new Node(data);
  10. current = head;
  11. }else
  12. {
  13. //创建新的结点
  14. Node node = new Node(data);
  15. //放在当前结点的后面
  16. current.next=node;
  17. //把链表的当前索引向后移动一位
  18. current=current.next;
  19. }
  20. }
  21. //获取单链表的长度
  22. public int getLength()
  23. {
  24. //头结点为空则返回0
  25. if(head==null)
  26. return 0;
  27. else
  28. {
  29. int length=0;
  30. Node current = head;
  31. while(current!=null)
  32. {
  33. current=current.next;
  34. length++;
  35. }
  36. return length;
  37. }
  38. }
  39. //打印链表
  40. public void printList(){
  41. Node tmp = head;
  42. while(tmp!=null)
  43. {
  44. System.out.println(tmp.data+" ");
  45. tmp=tmp.next;
  46. }
  47. }
  48. //删除第index个结点
  49. public boolean deleteNode(int index){
  50. if(index<1||index>this.getLength())
  51. return false;
  52. if(index==1){
  53. head=head.next;
  54. return true;
  55. }
  56. int i =1;
  57. Node proNode = head;
  58. Node curNode = proNode.next;
  59. while(curNode!=null)
  60. {
  61. if(i==index)
  62. {
  63. proNode.next=curNode.next;
  64. return true;
  65. }
  66. proNode = curNode;
  67. curNode = curNode.next;
  68. i++;
  69. }
  70. return false;
  71. }
  72. //链表反转 遍历实现
  73. public static Node Reverse(Node head)
  74. {
  75. if(head==null||head.next==null)
  76. return head;
  77. Node pre=head; //上一结点
  78. Node cur=head.next; //当前结点
  79. Node tmp ;//临时结点,用于保存当前指针域
  80. while(cur!=null)
  81. {
  82. tmp=cur.next;
  83. cur.next=pre; //反转指向
  84. //指针向下移动
  85. pre=cur;
  86. cur=tmp;
  87. }
  88. head.next=null ;//将头结点设空
  89. return pre;
  90. }
  91. //判断单链表是否有环,我们用两个指针去遍历:
  92. //first指针每次走一步,second指针每次走两步,如果first指针和second指针相遇,说明有环。
  93. public boolean hasCycle(Node head) {
  94. if (head == null) {
  95. return false;
  96. }
  97. Node first = head;
  98. Node second = head;
  99. while (second != null) {
  100. first = first.next; //first指针走一步
  101. second = second.next.next; //second指针走两步
  102. if (first == second) { //一旦两个指针相遇,说明链表是有环的
  103. return true;
  104. }
  105. }
  106. return false;
  107. }
  108. }

五. 双向链表简单操作

  1. public class DoubleLinkList {
  2. //头
  3. private Node first;
  4. //尾
  5. private Node last;
  6. public DoubleLinkList(){
  7. first = null;
  8. last = null;
  9. }
  10. /**
  11. * 插入数据
  12. * @param value
  13. */
  14. public void insertFirst(long value){
  15. Node newNode = new Node(value);
  16. if (first == null) {
  17. last = newNode;
  18. }else {
  19. first.previous = newNode;
  20. //把first节点往下移动
  21. newNode.next = first;
  22. }
  23. //把插入的节点作为新的节点
  24. first = newNode;
  25. }
  26. /**
  27. * 插入数据
  28. * @param value
  29. */
  30. public void insertLast(long value){
  31. Node newNode = new Node(value);
  32. if (first == null) {
  33. first = newNode;
  34. }else {
  35. last.next = newNode;
  36. //first.previous = newNode;
  37. newNode.previous = last;
  38. }
  39. //把最后个节点设置为最新的节点
  40. last = newNode;
  41. }
  42. public boolean isEmpty(){
  43. return first == null;
  44. }
  45. /**
  46. * 删除头节点时要去除两个指针,一个指向下个的next ,一个是next的previous指向前面的
  47. *
  48. * @param value
  49. * @return
  50. */
  51. public Node deleteFirst(){
  52. if (first == null) {
  53. throw new RuntimeException("链表数据不存在");
  54. }
  55. Node temp = first;
  56. if (first.next == null) {
  57. last = null;
  58. }else {
  59. first.next.previous = null;
  60. }
  61. first = temp.next;
  62. return temp;
  63. }
  64. /**
  65. * 删除头节点时要去除两个指针,一个指向下个的next ,一个是next的previous指向前面的
  66. *
  67. * @param value
  68. * @return
  69. */
  70. public Node deleteLast(){
  71. if (first == null) {
  72. throw new RuntimeException("链表数据不存在");
  73. }
  74. Node temp = last;
  75. if (first.next == null) {
  76. last = null;
  77. //把第一个删除
  78. first = null;
  79. }else {
  80. last.previous.next = null;
  81. }
  82. last = temp.previous;
  83. return temp;
  84. }
  85. /**
  86. * 删除
  87. * @param key
  88. * @return
  89. */
  90. public Node deleteByKey(long key){
  91. Node current = first;
  92. while(current.data != key){
  93. if (current.next == null) {
  94. System.out.println("没找到节点");
  95. return null;
  96. }
  97. current = current.next;
  98. }
  99. if (current == first) {
  100. //return deleteFirst();
  101. //指向下个就表示删除第一个
  102. first = first.next;
  103. }else {
  104. current.previous.next = current.next;
  105. }
  106. return current;
  107. }
  108. /**
  109. * 显示所有的数据
  110. */
  111. public void display(){
  112. if (first == null) {
  113. //throw new RuntimeException("链表数据不存在");
  114. return;
  115. }
  116. Node current = first;
  117. while(current != null){
  118. current.display();
  119. current = current.next;
  120. }
  121. System.out.println("---------------");
  122. }
  123. /**
  124. * 查找节点1
  125. * @param value
  126. * @return
  127. */
  128. public Node findByValue(long value){
  129. Node current = first;
  130. while(current != null){
  131. if (current.data != value) {
  132. current = current.next;
  133. }else {
  134. break;
  135. }
  136. }
  137. if (current == null) {
  138. System.out.println("没找到");
  139. return null;
  140. }
  141. return current;
  142. }
  143. /**
  144. * 查找节点2
  145. *
  146. * @param key
  147. * @return
  148. */
  149. public Node findByKey(long key) {
  150. Node current = first;
  151. while (current.data != key) {
  152. if (current.next == null) {
  153. System.out.println("没找到");
  154. return null;
  155. }
  156. current = current.next;
  157. }
  158. return current;
  159. }
  160. /**
  161. * 根据索引查找对应的值
  162. * @param position
  163. * @return
  164. */
  165. public Node findByPosition(int position){
  166. Node current = first;
  167. //为什么是position - 1,因为要使用遍历,让current指向下一个, 所以position - 1的下个node就是要找的值
  168. for (int i = 0; i < position - 1 ; i++) {
  169. current = current.next;
  170. }
  171. return current;
  172. }
  173. }

剑指 Offer 18. 删除链表的节点

public class DeleteGivenValue {

    public static class ListNode {
        public int val;
        public ListNode next;

        public ListNode(int data) {
            this.val = data;
        }
    }

    // head = removeValue(head, 2);
    public ListNode deleteNode(ListNode head, int num) {
        // head来到第一个不需要删的位置
        while (head != null) {
            if (head.val != num) {
                break;
            }
            head = head.next;
        }
        // 1 ) head == null
        // 2 ) head != null
        ListNode pre = head;
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == num) {
                pre.next = cur.next;
            } else {
                pre = cur;
            }
            cur = cur.next;
        }
        return head;
    }
}

六. 静态链表

package com.yds.list;
/**
 * 因为静态链表实质上是一维数组的存储方式,所以它在物理位置上的存储
 * 是顺序的,但它是用游标来指向下一个数据的,所以根据它的下标来获取数据
 * 和按照游标的指向来获取数据是不同的,这里设置该链表的头结点为空
 * @author Administrator
 *
 */
public class StaticLinkList {

    private Element[] linkList = null; 
//  private Element[] linkList1 = null;
    private int DEFAULT_SIZE = 4;//默认存储大小
    private int currentFree = 0;//指向当前空闲位置
    private int size = 1;
    class Element{
        int data;
        int cur;
    }
    /**
     * 静态链表的长度
     * @return
     */
    public int length(){
        return size-1;
    }
    /**
     * 静态链表的初始化
     */
    public StaticLinkList(){
        linkList = new Element[DEFAULT_SIZE];
        for (int i = 0; i < linkList.length; i++) {
            linkList[i] = new Element();
            linkList[i].data = -1;
            linkList[i].cur = i+1;
        }
        //当前空闲节点从1开始,因为第0个节点设置成了头结点,设置为空,不
        //存储数据
        currentFree = 1;
    }
    /**
     * 给链表添加数据,每当链表满了就给链表添加额外的空间
     * @param data
     */
    public void add(int data){
        if(size<linkList.length){
            linkList[currentFree].data = data;
            currentFree = linkList[currentFree].cur;
            size++;
        }else{//链表已满,给链表添加空间
            addLinkSpace();
            linkList[currentFree].data = data;
            currentFree = linkList[currentFree].cur;
            size++;
        }
    }
    /**
     * 得到索引指定的数据
     * @param index
     * @return
     */
    public int get(int index){
        if(index>size-1&&index<0)
            throw new IndexOutOfBoundsException("数组越界,索引不合法");
        else{
            //这里index+1也是因为多了一个空的头节点
            return linkList[index+1].data;
        }
    }
    /**
     * 删除指定位置的节点
     * @param index
     */
    public void delete(int index){

        index = index+1;
        if(index<1||index>=size){
            System.out.println("超出链表长度");
        }else if(index == size-1){
            size--;
            linkList = (Element[]) getTrueIndex(linkList,size);
        }else{
            int i = 0;
            while(index!=linkList[i].cur){
                i++;
            }
            int j = 0;
            while(currentFree!=linkList[j].cur){
                j++;
            }
            linkList[i].cur = linkList[index].cur;
            linkList[j].cur = index;
            linkList[index].cur = currentFree;
            currentFree = index;
            size--;
            linkList = (Element[]) getTrueIndex(linkList,size);
        }
    }
    /**
     * 增加链表空间
     */
    public void addLinkSpace(){
        DEFAULT_SIZE+=8;
        Element[] link = linkList;
        linkList = new Element[DEFAULT_SIZE];
        System.arraycopy(link, 0, linkList, 0, link.length);
        for (int i = link.length; i < DEFAULT_SIZE; i++) {
            linkList[i] = new Element();
            linkList[i].data = -1;
            linkList[i].cur = i+1;
        }
        currentFree = link.length;
    }

    /**
     * 根据指定的位置插入数据
     * @param index
     * @param data
     */
    public void insert(int index,int data){
        //这里加1的原因是因为链表的第0位为空节点,这里设置的头节点为空
        index = index + 1;
        if(size<linkList.length){
            if(index>size&&index<0)
                System.out.println("数组越界,超出数组长度");
            else if(index == size){
                linkList[currentFree].data = data;
                currentFree = linkList[currentFree].cur;
                size++;
            }else{
                /******未按逻辑顺序排序而插入数据的写法,因为未排序,则当前索引的上个节点的索引不一定是当前索引减1****/
                int i = 0;
                while(index!=linkList[i].cur){
                    i++;
                }
                int j = 0;
                while(currentFree!=linkList[j].cur){
                    j++;
                }
                linkList[i].cur = currentFree;
                linkList[j].cur = linkList[currentFree].cur;
                linkList[currentFree].data = data;
                linkList[currentFree].cur = index;
                currentFree = linkList[j].cur;
                size++;
                //每次插入后将链表按逻辑顺序重新排序,是为了方便输出查看。
                linkList = (Element[]) getTrueIndex(linkList,size);
            }
        }else{
            addLinkSpace();
            insert(index, data);
        }
    }
    /**
     * 按照逻辑顺序重新排列
     * @param link 
     * @return
     */
    public Object getTrueIndex(Element[] link,int size){
        Element[] linkList1 = new Element[linkList.length];
        int k =0;
        for (int i = 0; i < linkList.length; i++) {
            linkList1[i] = new Element();
            linkList1[i].data = link[k].data;
            k = link[k].cur;
            linkList1[i].cur = i+1;
        }
        //插入时,currentFree肯定是最后一个了,但删除后,currentFree就不一定是最后一位了
        currentFree = size;
        return linkList1;
    }
}

七. 单链表实现栈和队列

队列 : 先进先出

栈 : 先进后出 , 后进先出

时间复杂度都是 O(1)

队列结构

public static class Node<V> {
   public V value;
   public Node<V> next;

   public Node(V v) {
      value = v;
      next = null;
   }
}

public static class MyQueue<V> {
   private Node<V> head;
   private Node<V> tail;
   private int size;

   public MyQueue() {
      head = null;
      tail = null;
      size = 0;
   }

   public boolean isEmpty() {
      return size == 0;
   }

   public int size() {
      return size;
   }

   public void offer(V value) {
      Node<V> cur = new Node<V>(value);
      if (tail == null) {
         head = cur;
         tail = cur;
      } else {
         tail.next = cur;
         tail = cur;
      }
      size++;
   }

   // C/C++的同学需要做节点析构的工作
   public V poll() {
      V ans = null;
      if (head != null) {
         ans = head.value;
         head = head.next;
         size--;
      }
      if (head == null) {
         tail = null;
      }
      return ans;
   }

   // C/C++的同学需要做节点析构的工作
   public V peek() {
      V ans = null;
      if (head != null) {
         ans = head.value;
      }
      return ans;
   }

}

栈结构

public static class MyStack<V> {
   private Node<V> head;
   private int size;

   public MyStack() {
      head = null;
      size = 0;
   }

   public boolean isEmpty() {
      return size == 0;
   }

   public int size() {
      return size;
   }

   public void push(V value) {
      Node<V> cur = new Node<>(value);
      if (head == null) {
         head = cur;
      } else {
         cur.next = head;
         head = cur;
      }
      size++;
   }

   public V pop() {
      V ans = null;
      if (head != null) {
         ans = head.value;
         head = head.next;
         size--;
      }
      return ans;
   }

   public V peek() {
      return head != null ? head.value : null;
   }

}

双端队列

public class Code03_DoubleLinkedListToDeque {

   public static class Node<V> {
      public V value;
      public Node<V> last;
      public Node<V> next;

      public Node(V v) {
         value = v;
         last = null;
         next = null;
      }
   }

   public static class MyDeque<V> {
      private Node<V> head;
      private Node<V> tail;
      private int size;

      public MyDeque() {
         head = null;
         tail = null;
         size = 0;
      }

      public boolean isEmpty() {
         return size == 0;
      }

      public int size() {
         return size;
      }

      public void pushHead(V value) {
         Node<V> cur = new Node<>(value);
         if (head == null) {
            head = cur;
            tail = cur;
         } else {
            cur.next = head;
            head.last = cur;
            head = cur;
         }
         size++;
      }

      public void pushTail(V value) {
         Node<V> cur = new Node<>(value);
         if (head == null) {
            head = cur;
            tail = cur;
         } else {
            tail.next = cur;
            cur.last = tail;
            tail = cur;
         }
         size++;
      }

      public V pollHead() {
         V ans = null;
         if (head == null) {
            return ans;
         }
         size--;
         ans = head.value;
         if (head == tail) {
            head = null;
            tail = null;
         } else {
            head = head.next;
            head.last = null;
         }
         return ans;
      }

      public V pollTail() {
         V ans = null;
         if (head == null) {
            return ans;
         }
         size--;
         ans = tail.value;
         if (head == tail) {
            head = null;
            tail = null;
         } else {
            tail = tail.last;
            tail.next = null;
         }
         return ans;
      }

      public V peekHead() {
         V ans = null;
         if (head != null) {
            ans = head.value;
         }
         return ans;
      }

      public V peekTail() {
         V ans = null;
         if (tail != null) {
            ans = tail.value;
         }
         return ans;
      }

   }

八. 题

1. 合并两个有序链表

方法一:迭代

public class Code06_MergeTwoSortedLinkedList {

   // 不要提交这个类
   public static class ListNode {
      public int val;
      public ListNode next;
   }

  class Solution {
     public static ListNode mergeTwoLists(ListNode head1, ListNode head2) {
       //如果其中有一个链表为空,直接返回另一个
      if (head1 == null || head2 == null) {
         return head1 == null ? head2 : head1;
      }
      //头指针指向两个链表头节点值较小的那个,抓到链表头结点用于返回结果
      ListNode head = head1.val <= head2.val ? head1 : head2;
      //用cur1记录头节点的下一个节点
      ListNode cur1 = head.next;
      //cur2指向另一个链表的头结点
      ListNode cur2 = head == head1 ? head2 : head1;
      //pre动态指针指向头结点
      ListNode pre = head;
      //当cur1和cur2都不为空的时候进入循环
      while (cur1 != null && cur2 != null) {
          //如果cur1的值小于等于cur2的值
         if (cur1.val <= cur2.val) {
             //将头结点的next指针指向cur1
            pre.next = cur1;
            //cur1向后移动
            cur1 = cur1.next;
            //如果cur1的值大于cur2的值
         } else {
             //将头节点的next指针指向cur2
            pre.next = cur2;
            //cur2向后移动
            cur2 = cur2.next;
         }
         //pre指针向后移动
         pre = pre.next;
      }
      //如果头结点的下一个节点为空,意味着其中合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
      pre.next = cur1 != null ? cur1 : cur2;
      //返回头指针
      return head;
   }

}

方法二:递归

class Solution {
    public static ListNode mergeTwoLists(ListNode head1, ListNode head2) {
        //如果head1为空,直接返回head2
        if(head1 == null){
            return head2;
        }
        //如果head2为空,直接返回head1
        if(head2 == null){
            return head1;
        }
        //如果head1的值小于head2的值
        if(head1.val < head2.val){
            //将head1的next指针指向(head1的下一个节点 和 head2)的合并
            head1.next = mergeTwoLists(head1.next,head2);
            //返回head1
            return head1;
        }
        //相反
        else{
            head2.next = mergeTwoLists(head1,head2.next);
            return head2;
        }

    }
}

2. 两数相加

public class Code05_AddTwoNumbers {

   // 不要提交这个类
   public static class ListNode {
      public int val;
      public ListNode next;

      public ListNode(int val) {
         this.val = val;
      }

      public ListNode(int val, ListNode next) {
         this.val = val;
         this.next = next;
      }
   }

   public static ListNode addTwoNumbers(ListNode head1, ListNode head2) {
      int len1 = listLength(head1);
      int len2 = listLength(head2);
      ListNode l = len1 >= len2 ? head1 : head2;
      ListNode s = l == head1 ? head2 : head1;
      ListNode curL = l;
      ListNode curS = s;
      ListNode last = curL;
      int carry = 0;
      int curNum = 0;
      while (curS != null) {
         curNum = curL.val + curS.val + carry;
         curL.val = (curNum % 10);
         carry = curNum / 10;
         last = curL;
         curL = curL.next;
         curS = curS.next;
      }
      while (curL != null) {
         curNum = curL.val + carry;
         curL.val = (curNum % 10);
         carry = curNum / 10;
         last = curL;
         curL = curL.next;
      }
      if (carry != 0) {
         last.next = new ListNode(1);
      }
      return l;
   }

   // 求链表长度
   public static int listLength(ListNode head) {
      int len = 0;
      while (head != null) {
         len++;
         head = head.next;
      }
      return len;
   }

}

25. K 个一组翻转链表

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode start = head;
        ListNode end = getKGroupEnd(start, k);
        if (end == null) {
            return head;
        }
        // 第一组凑齐了!
        head = end;
        reverse(start, end);
        // 上一组的结尾节点
        ListNode lastEnd = start;
        while (lastEnd.next != null) {
            start = lastEnd.next;
            end = getKGroupEnd(start, k);
            if (end == null) {
                return head;
            }
            reverse(start, end);
            lastEnd.next = end;
            lastEnd = start;
        }
        return head;
    }

    public static ListNode getKGroupEnd(ListNode start, int k) {
        while (--k != 0 && start != null) {
            start = start.next;
        }
        return start;
    }

    public static void reverse(ListNode start, ListNode end) {
        end = end.next;
        ListNode pre = null;
        ListNode cur = start;
        ListNode next = null;
        while (cur != end) {
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        start.next = end;
    }
}

26. 删除有序数组中的重复项

class Solution {
    public int removeDuplicates(int[] nums) {
        int n = nums.length;
        if (n == 0) {
            return 0;
        }
        int fast = 1, slow = 1;
        while (fast < n) {
            if (nums[fast] != nums[fast - 1]) {
                nums[slow] = nums[fast];
                ++slow;
            }
            ++fast;
        }
        return slow;
    }
}