链表
注意:
【1】链表的插入和删除不是所有情况下都比顺序表快,比如尾插尾删,顺序表的时间复杂度为O(1),并且如果是单链表,如果要在中间某个节点的前面插入/删除一个节点,则需要遍历。所以时间的快慢要分情况看待。
数组是在物理和逻辑上都连续,而,链表是只在逻辑上连续,但在物理上不连续的
在这个的next域存储下一个的地址,这样我们就知道这个节点指向哪一个节点了
这种链表是:单向,不带头,非循环链表
这种链表我们一旦找到第一个,后面的节点我们就全都都知道了
链表分类
这两种重要!
单链表
有head指向的节点,我们叫这种链表是带头的
如果是循环的,那么最后一个节点的next域就不是null了,而是指向上图中箭头所指的这个节点的地址
链表的定义
链表的创键
链表的方法
方法一:链表的遍历,全部打印出来
相当于数组遍历,这个是让链表打印完全部值,往后走
但是这样写有一个非常严重的问题,如果你要让head走的话,那你最后就不知道head的到底指向哪一个;了,所以我们不行这么写,我们要让cur走,而不是让head走
这样走完我们head指向的永远是第一个节点,一定要这样写!!!!
方法二:查找是否包含值为key的value
方法三:得到单链表的长度
方法四:头插法(一般来说我们一般用头插法来创建链表)
将一个新的节点插入到当前头节点的前面
我们一般建议在插入的时候,先绑定后面的节点信息
方法五:尾插法
PS:因为头插法是放进去当第一个节点,所以不管链表是否为空,都可以插入,而尾插法是放进去当最后一个节点的,所以当链表为空的时候,不能null后面还有node,所以直接把node赋给cur
方法六:在指定位置插入元素
方法七:删除第一次出现的关键字key
方法八:清空链表
注意:
对于这道题我们要注意的问题就是,在插入s的时候,我们的顺序是先连接s,再断开p
练习题1
通过遍历一遍将要删除的数字都删掉,此处要删的是23;
如果要删的数字在第一个节点,那么我们就把循环全部走完之后再来判断是否第一个也为要删除的数字
讲了吗
双向链表
LinkedList的模拟实现
public class MyLinkedList {//双向链表和单链表最大的不同点就是双向链表自带一个last,就不用像单链表一样再遍历一遍
static class ListNode {
private int val;
private ListNode prev;
private ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;
public ListNode last;
public int size() {
ListNode cur = head;
int len = 0;
while (cur != null) {
len++;
cur = cur.next;
}
return len;
}
public void display() {
ListNode cur = head;
while (cur != null) {
System.out.print(cur.val + " ");
cur = cur.next;
}
}
public boolean contains(int key) {
ListNode cur = head;
while (cur != null) {
if (cur.val != key) {
cur = cur.next;
} else {
return true;
}
}
return false;
}
//头插法
public void addFirst(int data) {
ListNode node = new ListNode(data);
if (head == null) {
head = node;
last = node;
} else {
node.next = head;
head.prev = node;
head = node;
}
}
//尾插法
public void addLast(int data) {
ListNode node = new ListNode(data);
if (head == null) {
head = node;
last = node;
} else {
last.next = node;
node.prev = last;
last = node;
}
}
//任意位置插入,第一个元素下标为0
public void addIndex(int index, int data) {
cheakIndex(index);
if (index == 0) {
addFirst(data);
return;
}
if (index == size()) {
addLast(data);
return;
}
ListNode cur = searchIndex(index);
ListNode node = new ListNode(data);
node.next = cur;
cur.prev.next = node;
node.prev = cur.prev;
cur.prev = node;
}
private ListNode searchIndex(int index) {
ListNode cur = head;
while (index != 0) {
cur = cur.next;
index--;
}
return cur;
}
private void cheakIndex(int index) {//抛出不合法性
if (index < 0 || index > size()) {
throw new IndexOutOf("index不合法!" + index);//重要!抛出不合法
}
}
```java
//删除第一个关键字key的值
//分三步来,(1)删除头节点(2)删除尾节点(3)删除中间节点
public void remove(int key) {
ListNode cur = head;
while (cur != null) {
if (cur.val == key) {//找到要删除的key,用三步进行循环
if (cur == head) {//key为第一个节点
head = head.next;
if (head != null) {
head.prev = null;
} else {
last = null;//else说明这个双向链表只有一个节点,所以将last也置空
}
} else {//key为中间节点或者最后一个节点
if (cur.next != null) {
cur.prev.next = cur.next;
cur.next.prev = cur.prev;
} else {//删除key为最后一个节点
last = last.prev;
last.next = null;//老师写的是:cur.prev.next=cur.next;last=last.prev;
}
}
return;
} else {
cur = cur.next;
}
}
}
public void clear(){
ListNode cur=head;
while(cur!=null){
ListNode th=cur;
cur.prev=null;
cur.next=null;
cur=th.next;
}
head=null;//记得给他手动置空,不然last和head还在呢
last=null;
}
public static void main(String[] args) {
MyLinkedList myLinkedList=new MyLinkedList();
myLinkedList.addFirst(5);
myLinkedList.addFirst(4);
myLinkedList.addFirst(3);
myLinkedList.addFirst(2);
myLinkedList.addFirst(1);
myLinkedList.addLast(6);
//System.out.println(myLinkedList.size());
/*boolean ret=myLinkedList.contains(6);
System.out.println(ret);*/
myLinkedList.addIndex(2,9);
myLinkedList.display();
myLinkedList.remove(3);
myLinkedList.display();
}