
顺序表与链表的区别
- 顺序表在物理空间上是连续的,其底层逻辑由数据搭建,链表序列的每一个元素是通过结点上的next来实现逻辑上的连续
- 链表不存在扩容问题,顺序表的数组是指定的大小,当指定的大小已经满了,就会扩大容量,系统一般会以1.5倍扩容,如果没有充分利用就会浪费空间,造成内存碎片
- 链表插入和删除,时间复杂度与顺序表一样,总体都是O(n)级别,但是链表的效率比顺序表快很多,因为链表插入和删除不用挪数据,只用读数据,而顺序表插入一个数据和删除一个数据,后续序列的元素都需要跟着向前或者向后移动。不过尾插尾删操作顺序表效率高。
- 顺序表存在下标,支持随机访问,链表不支持随机访问
链表可以分为无头结点 含有头结点、循环 不循环、单向与双向。总共可以形成八种不同的链表结构。无头单向不循环链表结构简单基础,需重点掌握。
要想打印链表序列中的数据域data,必须要学会遍历链表,无论是打印链表,还是后面的删除插入链表,都需要遍历链表,打印链表可以通过改变head来遍历整个链表,但是删除和插入不能直接通过head来进行操作,防止找不到前面的结点。所以一般情况都借用一个中间变量cur,来遍历整个链表,打印时,循环时cur为空就结束。
public void display(){
ListNode cur = head;
while(cur != null){ //遍历链表
System.out.print(cur.data+" ");
cur=cur.next;
}
System.out.println();
}
遍历链表中序列元素的数据域即可,如果此时的key等于数据域上的数据,则返回true,反之返回false
public boolean checkIsContains(int key){
//这里无需判断是否为空,当为空时,直接返回false
ListNode cur = head;
while(cur != null){
if(cur.data==key){
return true;
}
cur=cur.next;
}
return false;
}
添加结点包括头插法,其时间复杂度时O(1),还有尾插和任意位置插入结点,这两种方式时间复杂度为O(n),需要遍历整个数组,任意位置插入结点,其逻辑大体就是以下操作
node.next=preNode.next;
preNode.next=node;
流程就是

public void AddFirst(int number){
ListNode node = new ListNode(number);
if(head==null){
head=node;
}else{
node.next=this.head;
head=node;
}
}
public void AddLast(int number){
ListNode node=new ListNode(number);
if(head==null){
head=node;
}else{
ListNode cur=this.head;
while(cur.next!=null){
cur=cur.next;
}
cur.next=node;
}
}

public void Add(int index,int number){
//判断下标的合法性
checkIndex(index);
ListNode node = new ListNode(number);
ListNode preNode = head;
for (int i = 0; i < index-1; i++) {
preNode=preNode.next;
}
if(index==0){
AddFirst(number);
return;
}else if(index==size()){
AddLast(number);
return;
}else{
node.next=preNode.next;
preNode.next=node;
}
}
public void checkIndex(int index){
if(index<0&&index>size()){
throw new MySingleListIndexOutOfException("index不合法");
}
}

public void subOneKeyNode(int key){
if(head==null){
System.out.println("链表为空");
return;
}
if(head.data==key){
head=head.next;
return;
}
//1. 找到要删除的结点的前一个结点
ListNode cur=this.head;
while(cur.next != null){
if(cur.next.data==key){
cur.next=cur.next.next;
return;
}
cur=cur.next;
}
System.out.println("链表中无data为key的元素");
}
public void subAllKeyNode(int key){
if(head==null){
return;
}
ListNode cur = head;
//判断第一个结点的data是否时key
while(cur.data == key){
head=head.next;
cur=head;
if(cur==null){
return;
}
}
while(cur.next != null){
if(cur.next.data==key){
cur.next=cur.next.next;
}else{
cur=cur.next;
}
}
清空结点,可以简单暴力的head=null,这样就丢失链表上的所用结点,但是如果想把每一个链表上的next置为空,就需要借助
public void clear(){
//head=null;
ListNode cur = head;
ListNode curNext=null;
while(cur != null){
curNext=cur.next;
cur.next=null;
cur = curNext;
}
head=null;
}