一. 几个概念
0x01 链表
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针连接次序实现的。链表是以结点的方式存储的,是链式存储。结点可以在运行时动态生成。链表是有序的列表,但其在内存中的存储是非连续、非顺序的。
0x02 结点
在链表数据结构中,我们把一个数据元素的存储映像称为结点(Node)。而一个结点包含存储数据元素的两部分信息:数据域(Data)和指针域(Next)。
0x03 数据域、指针域
数据域:存储数据元素信息的域;
指针域:存储直接后继位置的域,指向下一个结点。
0x04 直接前驱元素和直接后继元素
- 对于线性存储结构,假设线性表记为 ( 1 , 2 , . . . , n − 1 , n , n + 1... ) (1,2,...,n-1,n,n+1...) (1,2,...,n−1,n,n+1...)
数据元素 n n n的直接前驱元素: n − 1 n-1 n−1
数据元素 n n n的直接后继元素: n + 1 n+1 n+1 - 对于链表存储结构,如下图所示:
head.next表示为头结点head的后继结点;而head则为head.next的前驱结点。
0x05 链表分带头结点的链表和没有头结点的链表
- 首先,无论链表是否为空,头指针均不为空;即链表一旦被创建,则头指针便指向了它,头指针是链表的必要元素。头指针存放的是链表第一个结点的地址。
- 对于包含头结点的单链表,头指针存放的是头结点的存放地址,而头结点也包含数据域(data)和指针域(next),但头结点的数据域一般不存储任何信息,头结点的指针域包含的是首结点的地址。
- 对于不包含头结点的单链表,头指针存放的就是首节点的地址。
- 首节点:是存放链表中第一个有效元素的结点。
- 头结点是为了操作的统一和方便而引出的,放在第一个元素结点之前。有了头结点则在首节点前增加、删除元素就和其他结点的操作统一了。头结点不一定是链表必需要素。
二. 单链表结构与顺序存储结构的优缺点
1.存储分配方式
- 顺序存储结构用一段连续存储单元依次存储线性表的数据元素
- 单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素
2.时间性能
- 查找
– 顺序存储结构o(1)
– 单链表o(n) - 插入和删除
– 顺序存储结构需要平均移动表长一半的元素,时间复杂度o(n)
– 单链表在找到位置的指针后,插入和删除时间复杂度仅为o(1)
2.空间性能
- 顺序存储结构需要预分配存储空间,分大了,浪费,分小了易发生上溢
- 单链表不需要预分配存储空间,只要有就可以分配,元素个数也不受限制
三. 几道面试题
面试题1:获取单链表的有效结点个数(如果是带头结点的链表,不统计头结点)
public static int getLength(HeroNode head){
if(head.next == null){
return 0; // 空链表
}
int length = 0;
HeroNode cur = head.next;
while (cur != null) {
length++;
cur = cur.next; //遍历
}
return length;
}
面试题2:查找单链表中的倒数第k个结点【新浪面试题】
/*
思路:①编写一个方法,接收head结点,同时接收一个index
②index表示倒数第index个结点
③先把链表从头到尾遍历,得到链表的总长度 getLength
④得到size后,从链表的第一个结点开始,遍历(size-index)个,就可以得到
⑤找到,则返回该结点,否则返回null
*/
public static HeroNode findLastIndexNode(HeroNode head, int index){
//判断链表为空,返回null
if (head.next == null) {
return null; //没找到
}
//第一个遍历得到链表的长度(结点个数)
int size = getLength(head);
//第二次遍历size-index位置,就是倒数第index个结点
//先做一个index的校验
if (index <= 0 || index > size) {
return null;
}
HeroNode cur = head.next;
for(int i = 0; i < size-index; i++){
cur = cur.next;
}
return cur;
}
面试题3:单链表的反转【腾讯面试题】
/*
思路:①先定义一个结点reverseHead = new HeroNode();
②从头到尾遍历原来的链表,每遍历一个结点,就将其取出,并放在新链表的最前端
③将原来的链表的head.next = reverseHead.next
*/
public static void reverseList(HeroNode head){
//如果当前链表为空,或者只有一个结点,无需反转直接返回
if (head.next == null || head.next.next ==