1.单链表
1.1.单链表的定义
- 优点:离散存放(不需要连续的存储空间),改变容量方便
- 缺点:不支持随机存储,需要额外空间存放指针
typedef struct LNode{ //定义单链表节点类型
ElemType data; //每个节点存放一个数据元素
struct LNode *next; //指针指向下个节点
}LNode, *linkList;
要表示单链表的时候,只需申明一个头指针L,指向单链表的第一个节点
//*Lnode L; //强调是一个节点
linkList L; //强调是一个链表,代码可读性更强
1.2.不带头结点的单链表
typedef struct LNode { //定义单链表节点类型
Elemtype data; //每个节点存放一个数据元素
struct LNode *next; //指针指向下一个节点
}LNode, *linkList;
//初始化单链表
bool initList(linkList &L) {
//将表置空,防止脏数据
L = NULL;
return true;
}
//判断表空
bool empty(linkList L){
if (L == NULL) return true;
else return false;
}
void main() {
//申明一个指向单链表的指针
linkList L;
//初始化单链表
initList(L);
//后续代码
}
1.3.带头结点的单链表
typedef struct LNode {
Elemtype data;
struct LNode *next;
}LNode, *linkList;
bool initList(linkList& L) {
//分配一个头结点
L = (LNode*)malloc(sizeof(LNode));
//内存不足,分配失败
if (L == NULL) return false;
//当前表空,因此将头结点的next指针置空
L->next = NULL;
return false;
}
//判断表空
bool empty(linkList L){
if (L->next == NULL) return true;
else return false;
}
void main() {
//申明一个指向单链表的指针
linkList L;
//初始化单链表
initList(L);
//后续操作
}
1.4.单链表的插入
1.4.1. 带头结点
bool listInsert(linkList& L, Elemtype e, int i) {
//判断i值是否合法(判断是否过小)
if (i < 1) return false;
//申明一个LNode类型的指针*p,并使其指向linkList的头结点L
LNode *p = L;
//遍历单链表,找到要插入的位序
for (int j = 0; p != NULL && j < i - 1; j++) p = p->next;
//判断i值是否合法(判断是否过大)
if (p == NULL) return false;
//申明一个节点s,并为它分配内存空间和数据
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = e;
//在单链表中插入s
s->next = p->next;
p->next = s;
return true;
}
1.4.2.不带头结点
bool listInsert(linkList& L, int e, int i) {
//判断i值是否合法
if (i < 1) return false;
//i=1时为特殊情况,在表头插入元素
if (i == 1) {
//申明一个节点s,并为其分配空间和输入数据
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = e;
//将s插入表头,并且把L的指向改为s
s->next = L;
L = s;
return true;
}
//申明一个LNode*类型的指针p指向第一个节点,并使用其遍历单链表
LNode *p = L;
for (int j = 1; p != NULL && j < i - 1) p = p->next;
//i值不合法
if (p == NULL) return false;
//申明一个节点s,并为其分配空间和输入数据
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = e;
//将s插入单链表中
s->next = p->next;
p->next = s;
return true;
}
1.4.3.尾插法
//尾插法
bool insertNextNode(LNode *p, int e) {
//i值不合法
if (p == NULL) return false;
LNode *s = (LNode*)malloc(sizeof(LNode));
//内存分配失败
if (s == NULL) return false;
s->data = e;
//插入s
s->next = p->next;
p->next = s;
return true;
}
bool listInsert(linkList& L, int e, int i) {
if (i < 1) return false;
LNode *p = L;
for (int j = 0; p != NULL && j < i - 1) p = p->next;
//调用尾插法函数插入元素
return insertNextNode(p, e);
}
1.4.4.前插法
//前插法
bool insertPriorNode(LNode *p, int e) {
//i值不合法
if (p == NULL) return false;
LNode* s = (LNode*)malloc(sizeof(LNode));
//在p后插入一个新节点s
s->next = p->next;
p->next = s;
//将p的数据赋值给s,再将e赋值给p
s->data = p->data;
p->data = e;
return true;
}
1.5.单链表的删除
1.5.1.按位序删除(带头结点)
//按位序删除
bool listDelete(linkList& L, int i, Elemtype& e) {
//i值不合法
if (i < 1) return false;
//申明一个LNode类型的指针指向头结点
LNode* p = L;
//将指针指向第i - 1个元素
for (int j = 0; p != NULL && j < i - 1; j++) p = p->next;
//i值不合法
if (p == NULL) return false;
//第i个元素为空
if (p->next == NULL) return false;
//取回第i个元素的值
LNode* q = p->next;
e = q->data;
p->next = q->next;
free(q);
return true;
}
1.5.2.指定节点删除
//指定节点删除
bool deleteNode(LNode* p) {
//i值不合法
if (p == NULL) return false;
//申明一个节点q,并使其指向p->
LNode* q = p->next;
//将q的数据赋值给q
p->data = q->data;
//q的nxet指针赋值给q
p->netxt = q->next;
//释放q
free(q);
return true;
}
1.6.单链表按值查找
//按值查找
LNode* locateElem(linkList L, Elemtype e) {
LNode* p = L->next;
//从第一个元素遍历查找值为e的结点
for (int i = 1; p != NULL && p->data != e) p = p->next;
return p; //找到后返回该节点指针,否则返回NULL
}
1.7.求单链表长
//求表长
int length(linkList L) {
//申明一个LNode*类型的指针p,使其指向表中第一个节点
LNode* p = L->next;
//标记长度
int length = 0;
//遍历单链表
while (p != NULL) {
p = p->next;
length++;
}
return length;
}
1.8.单链表的建立
1.8.1.尾插法
linkList listTailInsert(linkList& L) {
Elemtype x;
//为单链表L开辟一个新的内存空间,作为头结点
L = (linkList)malloc(sizeof(linkList));
//申明两个指针,并让它们指向头结点
LNode* i = L, * j = L;
//输入数据
cin >> x;
//停止输入(0可以改为任意值)
while (x != 0) {
//开辟一个大小为LNode的内存空间,并让j指向它
j = (LNode*)malloc(sizeof(LNode));
//为j输入数据
j->data = x;
//将j插入单链表中
i->next = j;
//i后移至j
i = j;
//循环输出数据
cin >> x;
}
//将最后一个节点的next置空
i->next = NULL;
return L;
}
1.8.2.头插法
linkList listHeadInsert(linkList& L) {
//为头结点分配内存空间
L = (linkList)malloc(sizeof(linkList));
L->next = NULL;
LNode* i;
Elemtype x;
cin >> x;
whil(x != 0) {
//为节点i分配内存空间,并输入数据
i = (LNode*)malloc(sizeof(LNode));
i->data = x;
//将i插入表中
i->next = L->next;
L->next = i;
cin >> x;
}
return L;
}
2.双链表
2.1.双链表的初始化
typedef struct DNode {
Elemtype data;
struct DNode* prior, * next;
}DNode, *DLinkList;
//初始化双链表
bool initDLinkList(DLinkList& L) {
//为头结点分配空间
L = (DLinkList)malloc(sizeof(DLinkList));
//内存分配失败
if (L == NULL) return false;
//头结点的前后指针置空
L->prior = NULL;
L->next = NULL;
return true;
}
void testDLinkList() {
//申明一个双链表
DLinkList L;
//初始化双链表
initDLinkList(L);
//后续操作
}
2.2.双链表的插入
bool insertNode(DNode* p, DNode* s) {
//插入操作非法
if (p == NULL && s == NULL) return false;
s->next = p->next;
//特殊情况:p为双链表中最后一个节点
if (p->next != NULL) p->next->prior = s;
s->prior = p;
p->next = s;
return true;
}
2.3.双链表的删除、销毁
//删除双链表当前节点的下个节点
bool deleteNextDNode(DNode* p) {
//删除操作非法
if (p == NULL) return false;
//申明一个DNode*类型的指针q指向p的下一个节点
DNode* q = p->next;
//删除操作非法
if (q == NULL) return false;
//改变前后指针,并且释放p的下一个节点q的内存空间
p->next = q->next;
//q不是最后一个节点
if (q->next != NULL) q->next->prior = p;
free(q);
return true;
}
//销毁双链表
void destroyDLinkList(DLinkList &L){
//遍历双链表,并且释放下个节点
while(L->next != NULL) deleteNextDNode(L);
//释放头结点
free(L);
//头指针L置空
L = NULL;
}
3.循环链表
typedef struct LNode {
Elemtype data;
struct LNode *next;
}LNode, *linkList;
//初始化循环链表
bool initList(linkList& L) {
L = (linkList)malloc(sizeof(linkList));
//内存分配失败
if (L == NULL) return false;
//将L的next指针指向自己
L->next = L;
return true;
}
//判断表空
bool isEmpty(linkList L) {
if (L->next = L) return true;
else return false;
}
//判断表尾
bool isTail(LNode* p) {
if (p->next == L) return true;
else return false;
}
4.循环双链表
typedef struct DNode {
elemtype data;
struct DNode *prior, *next;
}DNode, DLinkList;
//初始化循环双链表
bool initDLinkList(DLinkList& L) {
L = (DNode*)malloc(sizeof(DNode));
if (L == NULL) return false;
L->next = L;
L->prior = L;
return true;
}
//判断表空
bool isEmpty(DLinkList L) {
if (L->next == L) return true;
else return false;
}
//判断表尾
bool isTail(DLinkList L, DNode *p) {
if (p->next == L) return true;
else return false;
}
void testDLinkList() {
DLinkList L;
initDLinkList(L);
}
5.链表和顺序表的区别
5.1.逻辑结构
顺序表和链表都属于线性表
5.2.存储结构
- 顺序表
- 优点:存储密度大,支持随机存储
- 缺点:需要一片连续的存储空间,改变容量不方便
- 链表
- 优点:支持离散存储,可以动态的修改容量
- 缺点:存储密度小(需要存放指针),不支持随机存储
5.3.基本操作
5.3.1.创建
- 顺序表:预分配一片连续的存储空间
- 静态分配:容量不可更改
- 动态分配:容量可以更改,但需要大量移动
- 链表:仅分配一个头结点(也可以仅声明一个头指针)
5.3.2.销毁
- 顺序表:修改length = 0,逻辑上销毁
- 静态分配:由系统自动回收
- 动态分配:需要手动free
- 链表:逐个free节点
5.3.3.增加/删除
- 顺序表:在增加/删除的位置后方逐一移动元素,时间复杂度为O(n)
- 链表:需找到该元素的所在位置,然后修改指针,时间复杂度为O(n)
- 虽然顺序表和链表的时间复杂度都为O(n),但是顺序表的操作为移动元素,链表的操作为查找元素,因此,顺序表所需时间远大于链表
5.3.4.查找
- 按位序查找
- 顺序表:由于有随机存储特性,因此,时间复杂度为O(1)
- 链表:需要按序遍历链表,因此,时间复杂度为O(n)
- 按值查找
- 顺序表:按序遍历顺序表,因此,时间复杂度为O(n),如果有序,可为O(lon2n)
- 链表:按序遍历链表,因此,时间复杂度为O(n)
6.王道课后题
void deleteX(linkList& L, int x) {
//递归出口,遍历到表中最后一个元素或者表为空时执行
if (L == NULL) return;
//当前元素不为x,&L = L->next,执行下一层递归
else if (L->data != x) {
deleteX(L->next, x);
}
else {
//当前元素为x,申明一个LNode*类型的指针p指向L
LNode* p = L;
//将L后移
L = L->next;
//释放指针p所指向的内存空间
free(p);
//进行下一层递归
deleteX(L, x);
}
}
void deleteX(linkList& L, elemType x) {
//申明一个LNode*型的指针p,并使其指向头结点的下一个节点
LNode* p = L->next;
while (p!= NULL) {
//当前节点的数据为x,则删除当前节点
if (p->data == x) {
if (p->next != NULL) {
LNode* q = p->next;
p->data = q->data;
p->next = q->next;
free(q);
}
else free(p);
}
//移动到下一个节点
else p = p->next;
}
return;
}
void deleteX(linkList& L, elemType x) {
//申明LNode*类型的指针p和指针q,并使其分别指向第一个节点和头结点
//申明LNode*类型的指针s
LNode* p = L->next, *q = L, *s;
while (p != NULL) {
if (p->data == x) {
//s指向p所指向的节点
s = p;
//q的next指针指向p的next指针
q->next = p->next;
//p指针后移
p = p->next;
//释放s节点
free(s);
}
else {
//p、q同步后移
p = p->next;
q = q->next;
}
}
return;
}
void reverse(linkList L) {
//申明一个单链表A,并为其头结点分配空间
linkList A = (LNode*)malloc(sizeof(LNode));
//申明一个指针p指向单链表L的第一个节点
LNode* p = L->next;
//遍历单链表L,并且将他的每一个节点插入到单链表A的第一个节点(前插法)
while (p != NULL) {
LNode* q = (LNode*)malloc(sizeof(LNode));
q->data = p->data;
q->next = A->next;
A->next = q;
//插入完成后,指针p后移
p = p->next;
}
//将指针q重新指向链表A的第一个节点
q = A->next;
//按序输出单链表A
while (q != NULL) cout << q->data;
return;
}
void reverseCout(linkList L) {
//通过递归找到最后一个节点
if (L->next != NULL) reverseCout(L->next);
//输出除了头结点外的节点(逆序)
if (L != NULL) cout << L->data;
}
void deleteMin(linkList& L) {
if (L == NULL) return false;
//申明p,q指针,p用来遍历单链表,q用来标记当前最小值节点
LNode* p = L->next, * q = L->next;
//遍历单链表
while (p != NULL) {
//找到更小值,更新最小值和q的指向节点
if (p->data < q->data) q = p;
//p指针后移
p = p->next;
}
//删除最小值节点
p = q->next;
q->data = p->data;
free(p);
return true;
}
linkList deleteMin(linkList& L) {
//p指向第一个节点,pPre指向p节点的前驱
LNode* p = L->next, * pPre = L;
//min指向当前最小值节点,minPre指向当前最小值节点前驱
LNode* min = L->, * minPre = L;
//遍历单链表
while (p != NULL) {
//当前节点的值更小则更新最小值相关节点
if (p->data < min->data) {
min = p;
minPre = pPre;
}
//p相关节点后移
pPre = p;
p = p->next;
}
//删除最小值节点
minPre->next = min->next;
free(min);
return L;
}
linkList reverse(linkList& L) {
//p用来遍历单链表,s用来重新插入新节点
LNode* p = L->next, *s;
//将头结点的next指针置空
//1.防止最后一个节点的next指针是野指针
//2.因为p已经指向最开始单链表L的第一个节点,因此,不会丢失后继节点
L->next = NULL;
//遍历单链表
while (p != NULL) {
//新分配一块内存空间,并用s指向它
s = (LNode*)malloc(sizeof(LNode));
//将p赋值给s,并将s插入单链表的第一个节点
s->data = p->data;
s->next = L->next;
L->next = s;
//p后移一个节点
p = p->next;
}
return L;
}
linkList reverse(linkList& L) {
LNode* pre = L, * p = L->next, * q = p->next;
//p节点是第一个节点,因此,逆置后是最后一个节点,需要将其next指针置空
p->next = NULL;
//遍历单链表
while (q != NULL) {
//三个指针依次后移一位
pre = p;
p = q;
q = q->next;
//将p的next指针逆置
p->next = pre;
}
L->next = p;
return L;
}
- 设表为L-3-1-5-4
- 分裂成表1:L-3,表2:1-5-4
- 表1已经为有序表,则将遍历表2,依次将表2的节点插入表1的适当位置
linkList sequence(linkList& L) {
LNode* p = L->next, * pre = L;
//特殊情况:表空
if (p == NULL) return L;
LNode* s = p->next, *q;
//分割成两个表,将p的next指针置空
p->next = NULL;
while (s != NULL) {
//遍历新链表找到第一个比p大的节点的前驱
while (p != NULL && s->data > p->data) {
pre = p;
p = p->next;
}
//将q指向s的当前节点,s向后移
q = s;
s = s->next;
//将q插入到新表中
q->next = pre->next;
pre->next = q;
//重置p和pre指针
p = L->next;
pre = L;
}
return L;
}
- 遍历单链表,判断当前节点是否属于区间内
- 属于则删除,不属于则向后遍历
linkList deleteXtoY(linkList& L, int min, int max) {
LNode* p = L->next, * pre = L;
//遍历单链表
while (p != NULL) {
//在区间内
if (p->data >= min && p->data <= max) {
//删除元素并释放空间,修改前驱节点的next指针
pre->next = p->next;
//申明一个新指针s指向p,并向后移动p,释放s
LNode* s = (LNode*)malloc(sizeof(LNode));
s = p;
p = p->next;
free(s);
}
//不在空间内
else {
//pre和p向后移
pre = p;
p = p->next;
}
}
return L;
}
- 分别获取两个链表的节点个数,并且得到其差值lengthGap
- 长链表向前移动lengthGap个节点,这样剩下的节点和短链表的个数一样
- 通过指针遍历剩下的长链表和短链表,并判断其值是否相等
//获取单链表节点个数
int getLen(linkList L) {
LNode* p = L->next;
int length = 0;
while (p != NULL) {
p = p->next;
length++;
}
return length;
}
linkList sameLNode(linkList& a, linkList& b) {
//
int len1 = getLen(a), len2 = getLen(b);
linkList longList, shortList;
//lengthGap用于标记两个链表节点个数差
int lengthGap;
//个数多的设置为长表,个数少的设置为短表,记录差值
if (len1 < len2) {
shortList = a->next;
longList = b->next;
lengthGap = len2 - len1;
}
else {
shortList = b->next;
longList = a->next;
lengthGap = len1 - len2;
}
//移动长表的指针,使得剩下的节点个数和短表一样
while (lengthGap != 0) {
longList = longList->next;
lengthGap--;
}
//遍历剩余链表,对比两个链表当前元素
while (longList != NULL) {
if (longList->data == shortList->data) {
//返回第一个公共节点的指针
return longList;
}
longList = longList->next;
shortList = shortList->next;
}
return NULL;
}
- 遍历单链表,每轮找到一个最小值节点,输出并释放该最小值节点
- 仅剩头结点时,释放头结点
void coutAndFree(linkList& head) {
LNode* p, * pre, * min, * minpre;
//遍历单链表直到仅剩头结点
while (head->next != NULL) {
//每次循环重置各指针位置,其中min默认为除头结点外的第一个节点
p = head->next;
pre = head;
min = p;
minpre = pre;
//特殊情况:表中只有一个元素,输出该元素
if (p->next == NULL) cout << p->data;
else {
//遍历单链表
while (p != NULL) {
//找到当前最小值更小的节点,更新最小值相关指针
if (p->data < min->data) {
min = p;
minpre = pre;
}
//p相关指针后移
pre = p;
p = p->next;
}
}
//输出这一轮中的最小值
cout << min->data;
//删除最小值节点,并释放空间
minpre->next = min->next;
free(min);
}
//释放头结点
free(head);
}
- 新建一个头结点B,并申明一个指针指向A的第一个节点
- 将A分裂成头结点A和其他节点
- 遍历A的其余节点,用一个变量标记奇偶,奇数则插入A,偶数插入B
linkList oddEven(linkList& A) {
//为B声明一个头结点,并分配为其分配空间,置空next指针
linkList B = (LNode*)malloc(sizeof(LNode));
B->next = NULL;
LNode* p = A->next, * q = B, * pre = A;
//标记奇偶
int count = 1;
//遍历单链表
while (p != NULL) {
//偶数
if (count % 2 == 0) {
//申明一个节点s,并为其分配空间
LNode* s = (LNode*)malloc(sizeof(LNode));
//将p赋值给s
s->data = p->data;
//将s插入单链表B中,并且q后移一位
q->next = s;
s->next = NULL;
q = s;
//p相关指针后移,并且释放p当前节点
pre->next = p->next;
free(p);
p = pre->next;
}
//奇数p相关指针后移一位
else {
pre = p;
p = p->next;
}
count++;
}
return B;
}
linkList oddEven(linkList& A) {
//为B声明一个头结点,并分配为其分配空间,置空next指针
linkList B = (LNode*)malloc(sizeof(LNode));
B->next = NULL;
//使得p指向链表A的第一个节点
LNode* p = A->next;
//设置指针指向链表的最后一个节点,采用尾插法(顺序插入)
LNode* aTail = A, * bTail = B;
//将A断链
A->next = NULL;
//count标记奇偶
int count = 0;
while (p != NULL) {
count++;
//偶数,将p插入表中,并且aTail向后移动
if (count % 2 == 0) {
aTail->next = p;
aTail = aTail->next;
}
//奇数,将p插入表中,并且bTail向后移动
else {
bTail->next = p;
bTail = bTail->next;
}
//p向后移动
p = p->next;
}
//A和B的最后一个节点next指针置空
aTail->next = NULL;
bTail->next = NULL;
return B;
}
采用上题思路,一个表采用头插法一个表采用尾插法
linkList seperate(linkList& A) {
//为B申明一个头结点,并将其next指针置空
linkList B = (LNode*)malloc(sizeof(LNode));
B->next = NULL;
//用p指针指向A的节点,防止断链
LNode* p = A->next, * aTail = A, * bTemp = B;
//A的next指针置空,相当于清空其节点
A->next = NULL;
int count = 0;
while (p != NULL) {
count++;
//偶数,采用头插法
if (count % 2 == 0) {
bTemp = p;
p = p->next;
bTemp->next = B->next;
B->next = bTemp;
}
//奇数,采用尾插法
else {
aTail->next = p;
aTail = aTail->next;
p = p->next;
}
}
//A表采用尾插法,需要置空最后一个节点的next指针
aTail->next = NULL;
return B;
}
- 将p用尾插法插入A中
- 向后移动,将p用头插法插入B中
linkList seperate(linkList& A) {
//创建链表B
linkList B = (LNode*)malloc(sizeof(LNode));
B->next = NULL;
LNode* p = A->next, * aTail = A, * q;
while (p != NULL) {
//p用尾插法插入A
aTail->next = p;
aTail = aTail->next;
//p向后移动一个节点,并用q标记p的next节点
p = p->next;
q = p->next;
//p用头插法插入B(逆序)
p->next = B->next;
B->next = p;
//p移动到q的位置
p = q;
}
//aTail的next指针置空
aTail->next = NULL;
return B;
}
- 设置三个指针,pre标记当前节点,p标记pre的后一个节点,通过比较两个节点的数值,达到目的
- 如果两个节点的值相同,则用temp指向p,p向后继续移动,释放temp,继续比较
- 如果两个节点的值不相同,则p和pre都向后移动
linkList deleleSameLNode(linkList& L) {
LNode* p = L->next, * pre, * temp;
//表中仅有一个元素
if (p->next == NULL) return L;
else {
//循环遍历单链表
while (p != NULL) {
//p和pre向后移动一个节点
pre = p;
p = p->next;
//找到第一个不与pre->data相等的节点,并且依次释放中间节点
while (p != NULL && p->data == pre->data) {
temp = p;
p = p->next;
free(temp);
}
//接上单链表
pre->next = p;
}
}
return L;
}
- 分别用指针p和q标记A和B头结点之后的第一个节点,然后将链表A清空,将B的头结点释放
- 判断p和q指向的节点的大小,小的则插入A,并向后移动一个节点,直到某个表空
- 输出剩下的那个表的剩余元素
linkList merge(linkList& A, linkList& B) {
//用p和q分别遍历A和B链表
LNode* p = A->next, * q = B->next, * temp;
//将A的next指针置空,使其成为一个空链表
A->next = NULL;
//释放B的头结点
free(B);
//遍历A和B
while (p != NULL && q != NULL) {
//如果当前q节点小,则用头插法将其插入A
if (q->data < p->data) {
temp = q->next;
q->next = A->next;
A->next = q;
q = temp;
}
//如果当前p节点小,则用头插法将其插入A
else {
temp = p->next;
p->next = A->next;
A->next = p;
p = temp;
}
}
//输出剩余表
if (q == NULL) {
while (p != NULL) {
temp = p->next;
p->next = A->next;
A->next = p;
p = temp;
}
}
//输出剩余表
if (p == NULL) {
while (q != NULL) {
temp = q->next;
q->next = A->next;
A->next = q;
q = temp;
}
}
return A;
}
- 设置两个指针p和q分别用来遍历链表A和B
- 判断p和q大小,相等则新建节点复制p的数据,并将其用尾插法插入C;不相等则后移较小的节点
linkList commonLNode(linkList& A, linkList& B) {
//创建单链表C
linkList C = (LNode*)malloc(sizeof(LNode));
C->next = NULL;
LNode* p = A->next, * q = B->next, * s = C;
//用p和q指针分别遍历链表A和B
while (p != NULL && q != NULL) {
//pq指向的节点data相等,则新建一个节点temp,并用尾插法插入链表C
//spq分别向后移动一个节点
if (q->data == p->data) {
LNode* temp = (LNode*)malloc(sizeof(LNode));
temp->data = q->data;
temp->next = NULL;
s->next = temp;
s = s->next;
p = p->next;
q = q->next;
}
//不相等,则更小的节点向后移动一个节点
else if (q->data < p->data) q = q->next;
else p = p->next;
}
//将C的最后一个节点的next指针置空
s->next = NULL;
return C;
}
思路同14
linkList commonLNode(linkList& A, linkList& B) {
LNode* p = A->next, * q = B->next, * s = A, * temp;
//置空A表
A->next = NULL;
//遍历原A表和原B表
while (p != NULL && q != NULL) {
//相等,则将原A表的元素插入A中,并释放原B中的节点
if (p->data == q->data) {
s->next = p;
s = s->next;
p = p->next;
temp = q;
q = q->next;
free(temp);
}
//不相等,释放较小的节点,并向后移动一个节点
else if (p->data < q->data) {
temp = p;
p = p->next;
free(temp);
}
else {
temp = q;
q = q->next;
free(temp);
}
}
//释放剩余表中的节点
while (p) {
temp = p;
p = p->next;
free(temp);
}
while (q) {
temp = q;
q = q->next;
free(temp);
}
//将表A的最后一个节点置空
s->next = NULL;
//释放B的头结点
free(B);
return A;
}
- 子序列即B中每一个节点都按序存在于A某个节点之后
- 设置pa pb指针各自指向A B链表
- 外层循环遍历链表A,每移动到一个新的A节点,则开始内层循环,匹配接下来的元素是否都相等,不相等则pa向后移动,pb重置为第一个节点
bool subsequence(linkList& A, linkList& B) {
LNode* pa = A->next, * pb = B, *temp;
//遍历链表A
while (pa) {
//用temp保存pa的下一个节点,用于下次遍历
temp = pa->next;
//重置pb为B头结点后的第一个节点
pb = B->next;
//遍历链表B
while (pb) {
//pa与pb不等则跳出循环
if (pb->data != pa->data) break;
//否则pa和pb都向后移动一个节点
pa = pa->next;
pb = pb->next;
}
//判断B是否为A的子序列的条件是是否在遍历完B之前跳出循环,即pb是否到B的最后一个节点指向的NULL
if (pb == NULL) return true;
//将pa重置为temp指向的节点,开启下一次循环
pa = temp;
}
return false;
}
用pq指针分别标记链表的头尾,遍历单链表,判断是否pq的data是否相等
int getLen(DLinkList L) {
int length = 0;
DNode* p = L;
while (p->next != L) {
p = p->next;
length++;
}
return length;
}
bool isSymmetry(DLinkList L) {
//获得该循环双链表的长度
int length = getLen(L);
//每次循环pq指针各移动一个节点,因此总长度要除以2
//这样也能解决奇偶数的问题
//奇数的最中间节点不管如何都不影响对称性
length /= 2;
//p指向第一个节点,q指向最后一个节点
DNode* p = L->next, * q = L->prior;
//通过length为标记判断是否走完整个链表
while (length) {
//pq不相等则退出循环,此时length不可能为0
if (p->data != q->data) break;
//每次移动length-1,p向后移动,q向前移动
else {
length--;
p = p->next;
q = q->prior;
}
}
//length不为0,则不对称;为0,则对称
if (length) return false;
else return true;
}
linkList merge(linkList& A, linkList& B) {
LNode* pa = A->next, * pb = B->next;
//pa和pb分别遍历到A和B的最后一个节点
while (pa->next != A) pa = pa->next;
while (pb->next != B) pb = pb->next;
//将B的第一个节点接到A的最后一个节点后
pa->next = B->next;
//修改B的最后一个节点的next指针为A的头结点
pb->next = A;
//释放B的头结点
free(B);
return A;
}
bool deleteAndDestroy(linkList& L) {
LNode* p, * pre, *min, *minpre;
//遍历单链表
while (L->next != L) {
pre = L;
p = L->next;
min = p;
minpre = pre;
//找到当前链表中最小的节点
while (p != L) {
if (p->data < min->data) {
min = p;
minpre = pre;
}
pre = p;
p = p->next;
}
cout << min->data << endl;
//在链表中删除该元素,并释放该节点
minpre->next = min->next;
free(min);
}
//释放头结点
free(L);
return true;
}
DLinkList Locate(DLinkList& L, int x) {
DNode* p = L->next, *ppre = L, *q, *qpre;
//遍历单链表,找到链表中x在当前链表中的位置
while (p->data != x) {
ppre = p;
p = p->next;
}
//频度+1
p->freq++;
//p不为最后一个节点
if (p->next) {
ppre->next = p->next;
p->next->prior = ppre;
}
//p为最后一个节点
else ppre->next = NULL;
//找到第一个频度小于等于p的节点
q = L->next, qpre = L;
while (p->freq < q->freq) {
qpre = q;
q = q->next;
}
//q不为最后一个节点
if (q->next) {
qpre->next = p;
q->prior = p;
p->next = q;
p->prior = qpre;
}
//q为最后一个节点
else {
qpre->next = p;
p->next = NULL;
}
return p;
}
bool isCircle(linkList L) {
//设置快慢指针
LNode* fast = L->next, * slow = L->next;
//快指针一次移动两个节点,慢指针一次移动一个节点
//循环结束条件为快慢指针相遇或者快指针碰到NULL
while (fast->next != NULL && fast != NULL) {
fast = fast->next->next;
slow = slow->next;
if (fast == slow) break;
}
//快指针结束时不为NULL则有环,返回true;否则无环,返回false
if (fast) return true;
else return false;
}
- 设置pq指针都指向头结点后的一个节点,p向后移动k个节点,若未能移动k个节点p就指向了NULL,则返回false,说明k的大于表的长度
- p在q移动到k个节点后,pq一起移动,直到p移动到NULL,此时q的位置就为倒数第k个节点,输出q
bool searchLNode(linkList L, int k) {
LNode* p = L->next, * q = L->next;
//使得p移动到第k个节点
while (k > 0 && p != NULL) {
p = p->next;
k--;
}
//k值大于表长
if (k > 0) return false;
//找到倒数第k个元素的位置
while (p) {
p = p->next;
q = q->next;
}
cout << q->data << endl;
return true;
}
int getLen(linkList& L) {
int length = 1;
LNode* p = L->next;
while (p->next) {
p = p->next;
length++;
}
return length;
}
linkList commonLNode(linkList& A, linkList& B) {
linkList longList, shortList;
//得到AB链表的长度
int lenA = getLen(A);
int lenB = getLen(B);
int lengthGap;
//设置长链表和短链表,并求其长度差值
if (lenA < lenB) {
longList = B;
shortList = A;
lengthGap = lenB - lenA;
}
else {
longList = A;
shortList = B;
lengthGap = lenA - lenB;
}
LNode* p = longList->next, * q = shortList->next,* ptemp, *qtemp;
//长表移动两表差值个节点
while (lengthGap) {
p = p->next;
lengthGap--;
}
//求公共节点
while (p) {
if (p->data == q->data) {
ptemp = p;
qtemp = q;
while (ptemp != NULL && ptemp->data == qtemp->data) {
ptemp = ptemp->next;
qtemp = qtemp->next;
}
if (ptemp == NULL) return p;
}
p = p->next;
q = q->next;
}
return NULL;
}
//获得绝对值
int inverseNum(int a) {
if (a >= 0) return a;
else return -a;
}
linkList deleteSameLNode(linkList& L, int n) {
//申请n+1个元素的数组,用于标记出现的数字的绝对值
int* arr = (int*)malloc(sizeof(int) * (n + 1));
//初始化数组元素为0
for (int i = 0; i < n + 1; i++) arr[i]= 0;
LNode* p = L->next, *pre = L, *temp;
//遍历单链表
while (p) {
//将其结果转换为绝对值
int i = inverseNum(p->data);
//该数出现过,则删除该节点
if (arr[i]) {
temp = p->next;
pre->next = p->next;
free(p);
p = temp;
}
//该数未出现过,pre和p向后移,并且在数组中标记
else {
arr[i]++;
pre = p;
p = p->next;
}
}
return L;
}
int getLen(linkList& L) {
int length = 1;
LNode* p = L->next;
while (p->next) {
p = p->next;
length++;
}
return length;
}
linkList rearrange(linkList& L) {
//获取表长
int length = getLen(L);
LNode* p = L, *q, *temp, *s;
int dist = 0;
//移动p,使p的下一个节点为下半链表的第一个节点
if (length % 2) dist = length / 2 + 1;
else dist = length / 2;
while (dist) {
p = p->next;
dist--;
}
//temp标记后半链表的第一个节点
q = p->next;
temp = q;
//将表分裂为前半和后半两个部分
p->next = NULL;
//将q指针移动到后半链表的最后一个节点
while (q->next) q = q->next;
//将后半链表进行逆序
while (temp != q) {
s = temp->next;
temp->next = q->next;
q->next = temp;
temp = s;
}
//将p重置为L的头结点后的第一个节点
//逆序后,q指向的就是后半链表逆序后的第一个节点
p = L->next;
//将表置空
L->next = NULL;
s = L;
//依次从两个表取节点采用尾插法插入L中
int count = 1;
while (count <= length) {
if (count % 2) {
temp = p->next;
s->next = p;
p = temp;
}
else {
temp = q->next;
s->next = q;
q = temp;
}
s = s->next;
count++;
}
//将表中最后一个节点的next指针置空
s->next = NULL;
return L;
}