郑州那个公司做网站好淘宝搜索关键词排名查询工具
1. 链表
链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。
链表中包含两种域:
1. 存储数值的域
2. 存储引用(相当于c语言中的地址)的域
2. 链表的分类
链表根据3种分类方式分类
1. 单向或双向
2. 带头或不带头
3. 循环或不循环
更据以上3种分类方式,共有8种。
3. 单向链表
这里我们实现的是单向不带头不循环的链表。
public class SingleLinkedList {//头插法public void addFirst(int data){}//尾插法public void addLast(int data){}//任意位置插入,第一个数据节点为0号下标public void addIndex(int index,int data){}//查找是否包含关键字key是否在单链表当中public boolean contains(int key){return false;}//删除第一次出现关键字为key的节点public void remove(int key){}//删除所有值为key的节点public void removeAllKey(int key){}//得到单链表的长度public int size(){return -1;}public void clear() {}}
3.1 内部类
首先我们要创建一跟内部类包含存储数值的域和存储引用的域,之后我们需要引用来把我们的链表串联起来,第一个节点用head来表示。
static class ListNode {public int val;public ListNode next;public ListNode(int val) {this.val = val;}}public ListNode head;
3.2 头插法
思路:
将插入节点的下一个节点(next)指向头节点(head)
把插入节点改为头节点
//头插法public void addFirst(int data) {ListNode node = new ListNode(data);node.next = head;head = node;}
3.3 尾插法
思路:
先判断链表是否为空(因为尾插涉及到next域,链表如果为空会空指针异常)
遍历链表,找到尾节点,使尾点的next指向尾插节点
//尾插法public void addLast(int data) {ListNode node = new ListNode(data);if(head == null) {head = node;return;}ListNode cur = head;while (cur.next != null) {cur = cur.next;}cur.next = node;}
3.4 任意位置插入,第一个数据节点为0号下标
思路:
判断下标是否违法
判断是否是头结点或尾结点
遍历链表找到插入下标
//任意位置插入,第一个数据节点为0号下标public void addIndex(int index, int data) {ListNode node = new ListNode(data);if (index < 0 || index >= size()) {System.out.println("index下标不合法");return;}if(index == 0) {addFirst(data);return;}if(index == size()) {addLast(data);return;} ListNode cur = head;while (index - 1 >= 0) {cur = cur.next;index--;}node.next = cur.next;cur.next = node;}
3.5 查找是否包含关键字key是否在单链表当中
思路:
遍历链表找到关键字即可
//查找是否包含关键字key是否在单链表当中public boolean contains(int key) {ListNode cur = head;while (cur != null) {if (cur.val == key) {return true;}cur = cur.next;}return false;}
3.6 删除第一次出现关键字为key的节点
思路:
判断链表是否为空,为空直接返回
判断头节点是否为要删的节点,是就直接头节点指向下一个节点
遍历链表找到要删的节点的前一个节点,当前节点的next指向下一个节点的next
//查找关键字keyprivate ListNode findNodeOfKey(int key) {ListNode cur = head;while (cur.next != null) {if (cur.next.val == key) {return cur;}cur = cur.next;}return null;}//删除第一次出现关键字为key的节点public void remove(int key) {if (head == null) {return;}if (head.val == key) {head = head.next;return;}ListNode cur = findNodeOfKey(key);if(cur == null) {return;}cur.next = cur.next.next;}
3.7 删除所有值为key的节点
思路:
判断链表是否为空,为空返回
遍历链表当前节点的val值为关键字key,prev的next指向cur的next
判断头节点的val是否为要删除为key的节点,是就直接头节点指向下一个节点
//删除所有值为key的节点public void removeAllKey(int key) {if (head == null) {return;}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;}}
3.8 得到单链表的长度
思路:
定义一个计数器,遍历链表,只要节点不为空,就计数器++
//得到单链表的长度public int size() {int len = 0;ListNode cur = head;while (cur != null) {len++;cur = cur.next;}return len;}
3.9 清空单链表
思路:
遍历链表,定义一个临时变量存储当前节点的下一个节点,然后将当前节点的next置为空,最 后将头节点置为空
public void clear() {ListNode cur = head;while (cur != null) {ListNode curN = cur.next;cur.next = null;cur = curN;}head = null;}
3.10 单向链表的优缺点
优点:对于增删查改操作简单
缺点:只能从头到尾遍历,只能找到后驱,不能找到前驱
4. 双向链表
这里我们实现一个双向不带头不循环的链表
public class MyLinkedList {//头插法public void addFirst(int data){ }//尾插法public void addLast(int data){}//任意位置插入,第一个数据节点为0号下标public void addIndex(int index,int data){}//查找是否包含关键字key是否在单链表当中public boolean contains(int key){}//删除第一次出现关键字为key的节点public void remove(int key){}//删除所有值为key的节点public void removeAllKey(int key){}//得到链表的长度public int size(){}//清空链表public void clear(){}}
4.1 内部类
和上面的单向链表差不多,只需新增一个存储前驱的域。第一个节点用head来表示,最后一个节点用last来表示。
static class ListNode {public int val;public ListNode next;public ListNode prev;public ListNode(int val) {this.val = val;}}
4.2 头插法
思路:
判断链表是否为空,是就将头节点,尾节点指向插入节点
否则,插入节点的next指向头节点,头节点的prev指向插入节点,头节点指向插入节点
//头插法public void addFirst(int data){ListNode node = new ListNode(data);if(head == null && last == null) {head = node;last = node;}else {node.next = head;head.prev = node;head = node;}}
4.3 尾插法
思路:
判断链表是否为空,是就将头节点,尾节点指向插入节点
否则,尾节点的后驱next指向插入节点,插入节点的前驱prev指向尾节点,尾节点指向插入节 点
//尾插法public void addLast(int data){ListNode node = new ListNode(data);if(head == null && last == null) {head = node;last = node;}else {last.next = node;node.prev = last;last = node;}}
4.4 任意位置插入,第一个数据节点为0号下标
思路:
判断下标是否合法,不合法直接返回
如果插入的位置是头节点,直接用头插法 ,如果插入的位置是尾节点,直接用尾插法
如果插入的位置是中间,遍历链表,找到插入的对应位置,然后插入节点的后驱next指向对应 节点,插入节点的前驱prev指向对应节点的前驱prev,对应节点的前驱prev的后驱next指向插 入节点,对应节点的前驱prev指向插入节点
//判断下标是否合法 public boolean checkIndex(int index) {if(index < 0 || index >= size()) {System.out.println("下标违法");return false;}return true;}//任意位置插入,第一个数据节点为0号下标public void addIndex(int index,int data){ListNode node = new ListNode(data);if(checkIndex(index) == false) {return;}if(index == 0) {addFirst(data);return;}if(index == size()) {addLast(data);return;}ListNode cur = head;while (index - 1 >= 0) {cur = cur.next;index--;}node.next = cur;node.prev = cur.prev;cur.prev.next = node;cur.prev = node;}
4.5 查找是否包含关键字key是否在单链表当中
思路:
遍历链表找到关键字即可
//查找是否包含关键字key是否在单链表当中public boolean contains(int key){ListNode cur = head;while(cur != null) {if(cur.val == key) {return true;}cur = cur.next;}return false;}
4.6 删除第一次出现关键字为key的节点
思路:
如果链表为空,直接返回
遍历链表,找关键字为key的节点,如果头节点是,直接头接点指向头节点的下一个节点,然 后把头节点的前驱prev置为空,如果是尾节点,尾节点指向尾节点的前驱prev,尾节点的后 驱next置为空,其他位置,删除节点的前驱prev的后驱next指向删除节点的后驱next,删除节 点的后驱next的前驱prev指向删除节点的前驱prev
//删除第一次出现关键字为key的节点public void remove(int key){if(head == null) {return;}ListNode cur = head;while(cur != null) {if(cur.val == key) {if(cur == head) {head = head.next;head.prev = null;}else {if(cur.next == null) {last = last.prev;last.next = null;}else {cur.prev.next = cur.next;cur.next.prev = cur.prev;}}return;}cur = cur.next;}}
4.7 删除所有值为key的节点
思路:
和删除第一次出现关键字为key的节点的思路一样
//删除所有值为key的节点public void removeAllKey(int key){if(head == null) {return;}ListNode cur = head;while(cur != null) {if(cur.val == key) {if(cur == head) {head = head.next;head.prev = null;}else {if(cur.next == null) {last = last.prev;last.next = null;}else {cur.prev.next = cur.next;cur.next.prev = cur.prev;}}}cur = cur.next;}}
4.8 得到链表的长度
思路:
定义一个计数器,遍历链表,只要节点不为空,就计数器++
//得到链表的长度public int size(){int count = 0;ListNode cur = head;while(cur != null) {count++;cur = cur.next;}return count;}
4.9 清空链表
思路:
遍历链表,定义一个临时变量存储当前节点的下一个节点,然后将当前节点的后驱next,前 驱prev置为空,最后将头节点,尾节点置为空
//清空链表public void clear(){ListNode cur = head;while(cur != null) {ListNode curNext = cur.next;cur.next = null;cur.prev = null;cur = curNext;}head = last = null;}
4.10 双向链表的优缺点
优点:有了前驱,增加链表的灵活型
缺点:删除节点操作复杂
5. LinkedList
1. LinkedList的底层使用了双向链表
2. LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问
3. LinkedList的任意位置插入和删除元素时效率比较高,时间复杂度为O(1)