一、链表的概念
链表也是一种线程结构。
链表存储数据时,在逻辑上是连续的,但是在物理空间上不连续。既然在物理空间上不连续,那我们不能使用下标来访问链表中的结点。
链表的分类
1.、单链表 每一个存储数据的结点,只能记录其直接后继结点的位置。
2.、双向链表 每一个存储数据的结点, 既可以记录其直接后继结点的位置,
也可以记录其直接前驱结点的位置
3、循环链表 在以上两种结构中做一些处理,使得他最后一个结点可以记录第一个结点的位置
二、头结点实现单链表
有一个头结点(栈区),头结点的数据域不存储有效数据,只是使用其指针域指向第一个存储数据的结点。
1、结构的声明
typedef int ElemType;
typedef struct Node
{
ElemType data; // 存储数据元素的
struct Node *next; // 存储下一个结点的地址的
}HeadList;
2、操作方法声明
void InitHeadList(HeadList * head);
int GetLength(HeadList *head);
void ShowList(HeadList *head);
bool InsertOFPos(HeadList *head, ElemType val, int pos);
bool InsertOFHead(HeadList *head, ElemType val);
bool InsertOFTail(HeadList *head, ElemType val);
bool DeleteOFPos(HeadList *head, int pos);
bool DeleteOFHead(HeadList *head);
bool DeleteOFTail(HeadList *head);
bool DeleteOFValue(HeadList *head, ElemType val);
void ClearHeadList(HeadList *head);
void DestroyHeadList(HeadList *head);
3、操作方法实现
初始化
void InitHeadList(HeadList * head)
{
if (head == NULL) exit(0);
head->next = NULL;
}
插入
bool InsertOFPos(HeadList *head, ElemType val, int pos)
{
if (head == NULL) exit(0);
if (pos < 0) return false;
HeadList *p = head;
while (pos && p != NULL)
{
pos--;
p = p->next;
}
if (p == NULL) return false;
HeadList *newNode = ApplyNode(val, p->next);
if (newNode == NULL) return false;
p->next = newNode;
return true;
}
bool InsertOFHead(HeadList *head, ElemType val)
{
if(head == NULL) exit(0);
return InsertOFPos(head, val, 0);
}
bool InsertOFTail(HeadList *head, ElemType val)
{
if(head == NULL) exit(0);
HeadList *p = head;
while(p->next != NULL) p = p->next;
p->next = ApplyNode(val, NULL);
return true;
}
求长度、输出
int GetLength(HeadList *head)
{
if (head == NULL) exit(0);
int length = 0;
HeadList *p = head->next;
while (p != NULL)
{
length++;
p = p->next;
}
return length;
}
void ShowList(HeadList *head)
{
if (head == NULL) exit(0);
HeadList *p = head->next;
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
删除元素
bool DeleteOFPos(HeadList *head, int pos)
{
if(head == NULL) exit(0);
HeadList *p = head;
while(pos && p->next != NULL)
{
pos--;
p = p->next;
}
if(p->next == NULL) return false;
HeadList *q = p->next;
p->next = q->next;
free(q);
return true;
}
bool DeleteOFHead(HeadList *head)
{
if(head == NULL) exit(0);
return DeleteOFPos(head, 0);
}
bool DeleteOFTail(HeadList *head)
{
if(head == NULL) exit(0);
if(head->next == NULL) return false;
HeadList *p = head, *q = head->next;
while(q->next != NULL)
{
p = q;
q = q->next;
}
p->next = NULL;
free(q);
return true;
}
bool DeleteOFValue(HeadList *head, ElemType val)
{
if(head == NULL) exit(0);
HeadList *p = head, *q = head->next;
while(q != NULL)
{
if(q->data == val)
{
p->next = q->next;
free(q);
q = p->next;
}
else
{
p = q;
q = q->next;
}
}
return true;
}
清空、销毁
void ClearHeadList(HeadList *head)
{
if(head == NULL) exit(0);
while(head->next != NULL)
DeleteOFHead(head);
}
void DestroyHeadList(HeadList *head)
{
if(head == NULL) exit(0);
ClearHeadList(head);
}
三、头指针实现单链表
只有一个结点指针,只用这个结点指针保存第一个数据结点的地址。
和头结点单链表实现的区别: 插入时为空链的特殊处理和删除第一个结点的特殊处理。
1、 结构声明
typedef int ElemType;
typedef struct Node
{
ElemType data; // 存储数据元素的
struct Node *next; // 存储下一个结点的地址的
}PointList;
2 、操作方法声明
void InitPointList(PointList **pp); // 初始化
bool InsertPointList(PointList **pp, ElemType val, int pos); // 按位置插入
bool InsertHead(PointList **pp, ElemType val); // 头插
bool InsertTail(PointList **pp, ElemType val); // 尾插
bool DeletePonitList(PointList **pp, int pos); // 按位置删除
bool DeleteHead(PointList **pp); // 头删
bool DeleteTail(PointList **pp); // 尾删
bool DeleteValue(PointList **pp, ElemType val); // 按值删除
void DestroyPointList(PointList **pp); // 销毁
3、方法实现
申请新的节点
static PointList* ApplyNode(ElemType val, PointList *nt)
{
PointList *s = (PointList *)malloc(sizeof(PointList));
if (s == NULL) return NULL;
s->data = val;
s->next = nt;
return s;
}
初始化
void InitPointList(PointList **pp)
{
if (pp == NULL) exit(0);
*pp = NULL;
}
插入
bool InsertHead(PointList **pp, ElemType val)
{
if (pp == NULL) exit(0);
*pp = ApplyNode(val, *pp);
if (*pp == NULL) return false;
return true;
}
bool InsertPointList(PointList **pp, ElemType val, int pos)
{
if (pp == NULL) exit(0);
if (pos < 0) return false;
if (*pp == NULL || pos == 0) return InsertHead(pp, val);
PointList *p = *pp;
while (pos > 1 && p != NULL)
{
pos--;
p = p->next;
}
if (p == NULL) return false;
PointList *newNode = ApplyNode(val, p->next);
if (newNode == NULL) return false;
p->next = newNode;
return true;
}
bool InsertTail(PointList **pp, ElemType val)
{
if (pp == NULL) exit(0);
if (*pp == NULL) return InsertHead(pp, val);
PointList *p = *pp;
while (p->next != NULL) p = p->next;
p->next = ApplyNode(val, NULL);
return false;
}
删除
bool DeleteHead(PointList **pp)
{
if (pp == NULL) exit(0);
if (*pp == NULL) return false;
PointList *q = *pp;
*pp = q->next;
free(q);
return true;
}
bool DeletePonitList(PointList **pp, int pos)
{
if (pp == NULL) exit(0);
if (*pp == NULL || pos < 0) return false;
if (pos == 0)
{
return DeleteHead(pp);
}
PointList *p = *pp;
while (pos > 1 && p->next != NULL)
{
pos--;
p = p->next;
}
if (p->next == NULL) return false; // pos越界
PointList *q = p->next;
p->next = q->next;
free(q);
return true;
}
bool DeleteTail(PointList **pp)
{
if (pp == NULL) exit(0);
if (*pp == NULL) return false;
if ((*pp)->next == NULL) return DeleteHead(pp);
PointList *p = *pp;
PointList *q = p->next;
while (q->next != NULL)
{
p = q;
q = q->next;
}
p->next = NULL;
free(q);
return true;
}
bool DeleteValue(PointList **pp, ElemType val)
{
if (pp == NULL) exit(0);
if (*pp == NULL) return false; // 进来就是空链表
while (*pp != NULL && (*pp)->data == val)
{
DeleteHead(pp);
}
if (*pp == NULL) return true; // 将链表删除成空链
PointList *p = *pp;
PointList *q = p->next;
while (q != NULL)
{
if (q->data == val)
{
p->next = q->next;
free(q);
q = p->next;
}
else
{
p = q;
q = q->next;
}
}
return true;
}
销毁
void DestroyPointList(PointList **pp)
{
if (pp == NULL)
exit(0);
while (*pp != NULL)
{
DeleteHead(pp);
}
}
将头指针转化为头结点链表进行操作 – 以按位置插入为例
//将不带头结点的链表转化为带头结点的链表
bool InsertPointList2(PointList **pp, ElemType val, int pos)
{
if (pp == NULL) exit(0);
if (pos < 0) return false;
PointList vmNode; // 虚拟头结点
vmNode.next = *pp;
PointList *p = &vmNode;
while (pos && p != NULL)
{
pos--;
p = p->next;
}
if (p == NULL) return false;
PointList *newNode = ApplyNode(val, p->next);
if (newNode == NULL)
return false;
p->next = newNode;
*pp = vmNode.next; // 将虚拟头结点的next域赋值给*pp
return true;
}
四、单链表节点面试题:
1、找到倒数第K个结点
思路:
1、正数的第length-k+1个
2、两个指针实现:p,q;q指针先走k个节点(并且判断一下k是否合法),
如果q不为空,则继续p和q同步向后走,直到q为空,p就是倒数第k个节点。
HeadList *FindReciK2(HeadList *head, int k)
{
if (head == NULL) exit(0);
HeadList *p = head, *q = head;
// 找到正数第k个节点,并且判断k是否合法
while (k && q != NULL)
{
q = q->next;
k--;
}
if (q == NULL) return NULL;
while (q != NULL)
{
q = q->next;
p = p->next;
}
return p;
}
2、O(1)下删除单链表结点P,P不为尾结点
思路:
将p->next 节点数据复制到p节点上,然后删除p->next节点就可以!
void DeletePNode(HeadList *head, HeadList *p)
{
if (head == NULL || p == NULL || p->next == NULL)
return;
HeadList *q = p->next;
p->data = q->data;
p->next = q->next;
free(q);
}
3、判断两个单链表是否相交,返回相交的第一个结点
思路1:
先计算两个链表的长度,然后让指针p先在长的链表上走 差值 个, 然后指针p和指向短的链表的指针q同步向后走,并判断p和q是否相等,如
果相等,则返回p。
HeadList *IsIntersect(HeadList *l1, HeadList *l2)
{
if (l1 == NULL || l2 == NULL) return NULL;
int len1 = GetLength(l1);
int len2 = GetLength(l2);
HeadList *p = len1 - len2 > 0 ? l1 : l2; // p指向的是长的链表
HeadList *q = len1 - len2 > 0 ? l2 : l1; // q指向的是短的链表
int diff = fabs(len1 - len2);
while (diff)
{
p = p->next;
diff--;
}
while (p != NULL)
{
if (p == q) return p;
p = p->next;
q = q->next;
}
return NULL;
}
思路2:
两个指针p和q分别指向两个链表,同步向后遍历,当一个指针为NULL,让他指向另一个链表,直到p和q相等。p和q相等的两种情况:
1、指向了第一个相交的节点
2、 p和q都为NULL。
HeadList *IsIntersect2(HeadList *l1, HeadList *l2)
{
if (l1 == NULL || l2 == NULL) return NULL;
HeadList *p = l1;
HeadList *q = l2;
while (p != q)
{
p = p == NULL ? l2 : p->next;
q = q == NULL ? l1 : q->next;
}
return p;
}
4、判断两个单链表是否有环,返回入环的第一个结点
思路: 快慢指针
HeadList *IsRing(HeadList *head)
{
if (head == NULL) exit(0);
HeadList *p = head;
HeadList *q = head;
while (p != NULL && p->next != NULL)
{
p = p->next->next; // 一次走两步
q = q->next;
if (p == q)
{
break;
}
}
if (p != q) return NULL;
q = head;
while (p != q)
{
p = p->next;
q = q->next;
}
return p;
}
5、将单链表原地逆置
void Reverse(HeadList *head)
{
if (head == NULL || head->next == NULL || head->next->next == NULL) return;
HeadList *s = NULL;
HeadList *p = head->next;
HeadList *q = p->next;
while (p != NULL)
{
p->next = s;
s = p;
p = q;
if (q != NULL)
{
q = q->next;
}
}
head->next = s;
}
// 两个指针实现, 利用head->next代替上面代码的s指针
void Reverse(HeadList *head)
{
if (head == NULL || head->next == NULL || head->next->next == NULL) return;
HeadList *p = head->next;
HeadList *q = p->next;
head->next = NULL;
while (p != NULL)
{
p->next = head->next;
head->next = p;
p = q;
if (q != NULL)
{
q = q->next;
}
}
}