04 链表(LinkedList)
链表是真正的动态数据结构,其数据存储在“结点(Node)”中,它不需要处理固定容量的问题。其缺点在于丧失了随机访问的能力,不支持快速查询,而数组是支持快速查询的。
1.链表接口的实现
1 /** 2 * @author 阿遠 3 * Date: 2019/1/15 4 * Time: 11:08 5 */ 6 public class LinkedList<E> { 7 8 9 private Node dummyhead; // 链表头 10 private int size; // 链表容量 11 private class Node{ 12 public E e; // 元素 13 public Node next; // 结点 14 15 private Node(E e, Node next) { 16 this.e = e; 17 this.next = next; 18 } 19 20 public Node(E e) { 21 this(e, null); 22 } 23 24 public Node() { 25 this(null, null); 26 } 27 28 @Override 29 public String toString() { 30 return e.toString(); 31 } 32 } 33 34 public LinkedList() { 35 dummyhead = new Node(null, null); 36 size = 0; 37 } 38 39 // 获取列表中的元素个数 40 public int getSize() { 41 return size; 42 } 43 44 // 返回链表是否为空 45 public boolean isEmpty() { 46 return size == 0; 47 } 48 49 //在链表的index(0-based)位置添加新的元素e 50 public void add(int index, E e) { 51 if (index < 0 || index > size) { 52 throw new IllegalArgumentException("Add failed.Illegal index."); 53 } 54 Node pre = dummyhead; 55 for (int i = 0; i < index; i ++) { 56 pre = pre.next; 57 } 58 // Node node = new Node(e); 59 // node.next = pre.next; 60 // pre.next = node; 61 pre.next = new Node(e, pre.next); 62 size ++; 63 } 64 //在链表头添加新的元素 65 public void addFirst(E e) { 66 add(0, e); 67 } 68 // 在链表的末尾添加新的元素 69 public void addLast(E e) { 70 add(size, e); 71 } 72 73 // 获得链表的第index个位置的元素 74 public E get(int index) { 75 if (index < 0 || index >= size) 76 throw new IllegalArgumentException("Get failed.Illegal index."); 77 Node cur = dummyhead.next; 78 for (int i = 0; i < index; i++) { 79 cur = cur.next; // 遍历找到index处的Node 80 } 81 return cur.e; 82 } 83 // 获得列表的第一个元素 84 public E getFirst() { 85 return get(0); 86 } 87 // 获得链表中最后一个元素 88 public E getLast() { 89 return get(size - 1); 90 } 91 92 // 修改列表中第index个位置的元素为e 93 public void set(int index, E e) { 94 if (index < 0 || index >= size) 95 throw new IllegalArgumentException("Update failed.Illegal index."); 96 Node cur = dummyhead.next; 97 for (int i = 0; i < index; i++) { 98 cur = cur.next; 99 } 100 cur.e = e; 101 } 102 103 // 查找链表中是否有元素e 104 public boolean contains(E e) { 105 Node cur = dummyhead.next; 106 while(cur != null) { 107 if (cur.e.equals(e)) { 108 return true; 109 } 110 cur = cur.next; 111 } 112 return false; 113 } 114 // 从列表中删除index位置处的元素,返回删除的元素 115 public E remove(int index) { 116 if (index < 0 || index >= size) 117 throw new IllegalArgumentException("Remove failed.Illegal index."); 118 Node prev = dummyhead; 119 for (int i = 0; i < index; i ++) { 120 prev = prev.next; 121 } 122 Node retNode = prev.next; 123 prev.next = retNode.next; 124 retNode.next = null; 125 size --; 126 127 return retNode.e; 128 } 129 // 从链表中删除第一个元素,返回删除的元素 130 public E removeFirst() { 131 return remove(0); 132 } 133 // 从链表中删除最后一个元素 134 public E removeLast() { 135 return remove(size - 1); 136 } 137 138 @Override 139 public String toString() { 140 StringBuilder res = new StringBuilder(); 141 // Node cur = dummyhead.next; 142 // while(cur != null) { 143 // res.append(cur + "->"); 144 // cur = cur.next; 145 // } 146 for (Node cur = dummyhead.next; cur != null; cur = cur.next) 147 res.append(cur + "->"); 148 res.append("NULL"); 149 return res.toString(); 150 } 151 }
2. 链表接口的测试
1 public class Main { 2 3 public static void main(String[] args) { 4 5 LinkedList<Integer> linkedList = new LinkedList<Integer>(); 6 for (int i = 0; i < 5; i ++) { 7 linkedList.addFirst(i); 8 System.out.println(linkedList); 9 } 10 System.out.println("在索引为2的位置插入元素: 666"); 11 linkedList.add(2, 666); 12 System.out.println(linkedList); 13 14 System.out.println("移除索引为2的位置的元素: 666"); 15 linkedList.remove(2); 16 System.out.println(linkedList); 17 18 System.out.println("移除链表头的元素:4"); 19 linkedList.removeFirst(); 20 System.out.println(linkedList); 21 22 System.out.println("移除链表尾的元素:0"); 23 linkedList.removeLast(); 24 System.out.println(linkedList); 25 } 26 }
测试结果:
0->NULL 1->0->NULL 2->1->0->NULL 3->2->1->0->NULL 4->3->2->1->0->NULL 在索引为2的位置插入元素: 666 4->3->666->2->1->0->NULL 移除索引为2的位置的元素: 666 4->3->2->1->0->NULL 移除链表头的元素:4 3->2->1->0->NULL 移除链表尾的元素:0 3->2->1->NULL
3.链表的复杂度分析
添加操作 addLast(e) O(n) //从链表头开始遍历 addFirst(e) O(1) add(index, e) O(n/2) = O(n) //从链表头开始遍历 删除操作 removeLast(e) O(n) //从链表头开始遍历 removeFirst(e) O(1) remove(index, e) O(n/2) = O(n) //从链表头开始遍历 修改操作 set(index) O(n) contains(e) O(n) 总结: 增 : O(n) 删 : O(n) 改 : O(n) 查 : O(n) 适合只操作链表头:O(1)
4.链表的实例(LeetCode)
题目:
删除链表中等于给定值 val 的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6 输出: 1->2->3->4->5
答案:
1 /** 2 * @author 阿遠 3 * Date: 2019/1/21 4 * Time: 11:01 5 */ 6 //public class ListNode { 7 // int val; 8 // ListNode next; 9 // ListNode(int x) { val = x; } 10 //} 11 public class Solution { 12 public ListNode removeElements(ListNode head, int val) { 13 // 传入的链表不为空,链表头就是val以及新的链表头又是val 14 while (head != null && head.val == val) { 15 // ListNode delNode = head; 16 // head = head.next; 17 // delNode.next = null; 18 head = head.next; 19 } 20 //如果链表中所有的结点都是相同的val,此时就已经全部删除,链表为空 21 if (head == null) 22 return null; 23 // 链表中间的部分删除 24 ListNode pre = head; 25 while (pre.next != null) { 26 if (pre.next.val == val) { 27 // ListNode delNode = pre.next; 28 // pre.next = delNode.next; 29 // delNode.next = null; 30 pre.next = pre.next.next; 31 } else { 32 pre = pre.next; 33 } 34 } 35 return head; 36 } 37 }