一、重点说明:C++中有对参数指针取地址的操作
(一)typedef说明
typedef struct LNode {//定义单链表节点类型
ElemType data;//每个节点存放一个数据元素
struct LNode* next;//指针指向下一个节点
}LNode, * LinkList;
typedef :将当前类型名称命名为新名称
typedef struct LNode LNode;//将 结构体struct LNode命名为LNode
typedef struct LNode* LinkList;//将 结构体指针struct LNode*命名为LinkList
(二)C++特有的对参数进行引用
如:
bool InitList_None(LinkList& L){
L = NULL;
return true;
}
int main(){
LinkList L;
InitList_Node(L);
}
1.初始化中参数:LinkList& L <=>(LNode*)(&L):相当于对原指针的调用,即两者存储地址相同,指向地址相同,main函数中的指针L==参数中的L, 所以初始化改变L时,main函数的L也在改变
2.若为LinkList L<=>(LNode*)L:相当于创建一个新指针当参数,即两者存储地址不同,指向地址相同,main函数中的指针L!=参数中的L, 所以初始化改变L时,main函数的L不改变
(三)使用情况说明:
当需要改变原指针地址位置时,使用第一种,引用原指针修改
但在修改链表时,一般都是保持头指针不变,增删改查都不需要改变头指针的地址
另外,例如上面头结点的初始化操作,是需要改变头结点的指针指向的,所以引用原指针直接进行修改,我们也可以不引用,通过返回一个指针地址,在主函数修改原指针的地址同样可以
如
LNode* InitList_None(LinkList L){
L = NULL;
return L;
}
int main(){
LinkList L;
L=InitList_Node(L);
}
(四)c语言的二重指针
c语言:c语言没有引用的写法,所以我们想实现c++引用的这种写法,可以用二重指针来引用,即传入指针的地址,参数为指向原指针的指针
参数LinkList *L<=>LNode **L;
bool InitList_None(LinkList *L){
*L = NULL;
return true;
}
int main(){
LinkList L;
L=InitList_Node(&L);
}
(五)多重指针说明:
int a = 1;:a赋值
a:a的值
&a:a的地址
int* p = &a;:指针p指向a的地址
*p:a的值
p:a的地址
&p:指针p的地址
int **q = &p;:指针q指向指针p的地址
**q:a的值
*q:p的地址
q:a的地址
&q:指针q的地址
(六)如何将C++引用改成c语言
对原指针取地址,出入操作函数,利用二重指针进行修改,可实现和c++引用同样的效果
(七)以删除所有值为x的节点为例
LNode* DeleteNodeX1(LinkList L, ElemType x) {
if (L == NULL) {
return NULL;
}
L->next = DeleteNodeX(L->next, x);
if (L->data == x) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s = L;
L = L->next;
free(s);
return L;
}
return L;
}
void DeleteNodeX2LinkList& L, ElemType x) {
if (L == NULL) {
return;
}
if (L->data == x) {
LNode* s = L;
L = L->next;
free(s);
DeleteNodeX1(L, x);
}else
DeleteNodeX1(L->next, x);
}
void DeleteNodeX3(LNode** head, int val) {
if (*head == NULL) {
return;
}
if ((*head)->data == val) {
LNode* s = *head;
*head = (*head)->next;
free(s);
DeleteNodeX3(head, val);
}
else {
DeleteNodeX3(&(*head)->next, val);
}
}
int main()
{
LinkList L;
InitList(L);
List_TailInsert(L);
Del_Min_Node(L);
cout << "反向打印数组" << endl;
R_Print(L);
//1和3c和c++通用,2c++独有
L = DeleteNodeX1(L, 3);
DeleteNodeX2(L, 3);
DeleteNodeX3(&L, 3);
cout << "打印数组" << endl;
PrintList(L);
cout << "len:" << Length(L) << endl;
DestroyList(L);
}
二、单链表
#pragma warning(disable : 4996)
typedef int ElemType;
#include <iostream>
using namespace std;
typedef struct LNode {//定义单链表节点类型
ElemType data;//每个节点存放一个数据元素
struct LNode* next;//指针指向下一个节点
}LNode, * LinkList;
//初始化表:无头结点
bool InitList_None(LinkList& L) {//对于以后操作麻烦,一般不推荐使用
L = NULL;
return true;
}
//初始化表:有头结点
bool InitList(LinkList& L) {
L = (LNode *)malloc(sizeof(LNode));
if (L == NULL)
return false;
L->next = NULL;
return true;
}
//头插法:无需初始化
LinkList List_HeadInsert(LinkList &L) {
L = (LinkList)malloc(sizeof(LNode));//创建头结点
L->next = NULL;
int x;
scanf("%d", &x);//插入的元素个数
while (x > 0) {
LNode* s = (LNode*)malloc(sizeof(LNode));//创建插入节点
scanf("%d", &s->data);//赋值
s->next = L->next;
L->next = s;//头结点连接s
x--;
}
return L;
}
//尾插法:无需初始化
LinkList List_TailInsert(LinkList& L) {
L = (LNode*)malloc(sizeof(LNode));//创建头结点
L->next = NULL;
int x;
scanf("%d", &x);//插入的元素个数
LNode *p = L;
while (x > 0) {
LNode* s = (LNode *)malloc(sizeof(LNode));//创建插入节点
scanf("%d", &s->data);//赋值
s->next = NULL;//最后一个节点无后继
p->next = s;//尾结点连接插入节点
p = s;//将插入节点作为尾结点
x--;
}
return L;
}
//按位序插入:在第i个位置插入元素
bool ListInsert(LinkList& L, int i, ElemType e) {
if (i < 1) {//i值不合法
return false;
}
LNode* p = L;//指针p指向当前扫描到的节点
while (p != NULL && i != 1) {//找到要插入节点的前一个位置
p = p->next;
i--;
}
if (p == NULL) {//i值不合法
return false;
}
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;//将节点s连接到p之后
return true;
}
//指定节点的后插操作:在p节点之后插入元素e
bool InsertNextNode(LNode* p, ElemType e) {
if (p == NULL)//如果指针p为空,无法插入
return false;
LNode* s = (LNode*)malloc(sizeof(LNode));
if (s == NULL)//如果创建插入节点失败,返回错误
return false;
s->data = e;//赋值
s->next = p->next;//将s的后继连接p的后继
p->next = s;//p的后继连接s
return true;
}
//指定节点的前插操作:在p节点之前插入元素e
bool InsertPriorNode(LNode* p, ElemType e) {
if (p == NULL)//如果指针p为空,无法插入
return false;
LNode* s = (LNode*)malloc(sizeof(LNode));//创建插入节点
if (s == NULL)//创建失败,返回错误
return false;
s->data = p->data;//将p节点的值赋给插入节点,将其作为后插节点即可
p->data = e;//将插入的值赋给p节点
s->next = p->next;
p->next = s;
return true;
}
//按位序删除。删除表L中第i个位置的元素, 并用e返回删除元素的值
bool ListDelete(LinkList L,int i, ElemType& e) {
if (i < 1)//删除位置错误
return false;
LNode* p = L;
while (p != NULL && i != 1) {//找到删除节点的前一个位置
p = p->next;
i--;
}
if (p==NULL || p->next == NULL)//如果前一个节点遍历到结尾,则删除位置错误
return false;
LNode* s = p->next;//s指向删除节点
p->next = s->next;//p的后继连接s的后继
e = s->data;//获取删除元素值
free(s);//删除节点
return true;
}
//指定节点的删除
bool DeleteNode(LNode* p) {//缺点:无法删除最后一个节点
if (p == NULL || p->next == NULL) {//如果要删除节点为空,不用删除
return false;
}
ElemType e = p->data;
p->data = p->next->data;//将后一个节点的值赋给要删除的节点
LNode* s = p->next;//删除下一个节点即可
p->next = s->next;//将当前节点的后继连接给下下个节点
free(s);//删除s节点
return true;
}
//按序号查找节点值
LNode* GetElem(LinkList L, int i) {
if (i < 0)
return NULL;
if (i == 0)
return L;
LNode* p = L->next;
while (p!=NULL&&i>1) {
p = p->next;
i--;
}
return p;
}
//按值查找表结点
LNode* LocateElem(LinkList L, ElemType e) {
LNode* p = L->next;
while (p && p->data != e) {
p = p->next;
}
return p;
}
//求表长
int Length(LinkList L) {
int len = 0;
LNode* p = L->next;
while (p) {
p = p->next;
len++;
}
return len;
}
//判空操作
bool Empty(LinkList L) {
return L->next == NULL;
}
//输出操作
void PrintList(LinkList L) {
LNode* p = L->next;
while (p) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
//清空操作
void ClearList(LinkList& L) {
LNode* p = L->next;
while (p) {
LNode* s = p;
p = p->next;
free(s);
}
L->next = NULL;
}
//销毁操作
void DestroyList(LinkList& L) {
LNode* p = L;
while (p) {
LNode* s = p;
p = p->next;
free(s);
}
L = NULL;
}
int main()
{
LinkList L;
InitList(L);
cout << Empty(L) << endl;
List_TailInsert(L);
cout << Empty(L) << endl;
PrintList(L);
cout << Length(L) << endl;
DestroyList(L);
}
三、双向链表
#pragma warning(disable : 4996)
typedef int ElemType;
#include <iostream>
using namespace std;
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->prior = L->next = NULL;
return true;
}
//双链表的插入
bool InsertNextDNode(DNode *p, DNode *s) {
if (p == NULL || s == NULL) {
return false;
}
s->next = p->next;//将s节点插入到p的后面
if (p->next != NULL) {
p->next->prior = s;
}
p->next = s;
s->prior = p;
return true;
}
//双链表的删除
bool DeleteNextDNode(DNode* p) {
if (p == NULL || p->prior == NULL) {//p不能为空,且不能是头结点
return false;
}
DNode* s = p;
p->prior->next = p->next;
if(p->next != NULL)
p->next->prior = p->prior;
free(p);
return true;
}
//双链表的头插法
DLinkList DList_HeadInsert(DLinkList& L) {
InitDLinkList(L);
int x;
scanf("%d", &x);
while (x > 0) {
DNode* s = (DNode*)malloc(sizeof(DNode));
s->next = s->prior = NULL;
scanf("%d", &s->data);
InsertNextDNode(L, s);
x--;
}
return L;
}
//双链表的尾插法
DLinkList DList_TailInsert(DLinkList& L) {
InitDLinkList(L);
int x;
scanf("%d", &x);
DNode* p = L;
while (x > 0) {
DNode* s = (DNode*)malloc(sizeof(DNode));
s->next = s->prior = NULL;
scanf("%d", &s->data);
InsertNextDNode(p,s);
p = s;
x--;
}
return L;
}
//双链表的销毁
void DestoryDList(DLinkList &L) {
while(L->next != NULL) {
DeleteNextDNode(L->next);
}
free(L);
L = NULL;
}
//打印
void PrintDList(DLinkList& L) {
if (L == NULL) {
printf("双链表不存在");
return;
}
DNode* p = L->next;
while (p != NULL) {
printf("%d ",p->data);
p = p->next;
}
printf("\n");
}
//判空
bool Empty(DLinkList L) {
return L->next == NULL;
}
//按值查找
DNode* LocateElem(DLinkList L, ElemType e) {
DNode* s = L->next;
while (s != NULL&&s->data != e) {
s = s->next;
}
return s;
}
//按位查找
DNode* GetElem(DLinkList L, int i) {
if (i < 0) {
return NULL;
}
if (i == 0) {
return L;
}
DNode* s = L->next;
while (s != NULL&&i > 1) {
i--;
s = s->next;
}
return s;
}
int main() {
DLinkList L;
InitDLinkList(L);
cout << Empty(L) << endl;
DList_TailInsert(L);
PrintDList(L);
DeleteNextDNode(L->next->next);
PrintDList(L);
DestoryDList(L);
PrintDList(L);
}
四、循环单链表
#pragma warning(disable : 4996)
typedef int ElemType;
#include <iostream>
using namespace std;
typedef struct CLNode {//定义单链表节点类型
ElemType data;//每个节点存放一个数据元素
struct CLNode* next;//指针指向下一个节点
}LNode, * CLinkList;
//初始化表
bool InitCList(CLinkList& L) {
L = (CLNode*)malloc(sizeof(CLNode));
if (L == NULL)
return false;
L->next = L;
return true;
}
//头插法
CLinkList CList_HeadInsert(CLinkList& L) {
L = (CLNode *)malloc(sizeof(CLNode));//创建头结点
L->next = L;
int x;
scanf("%d", &x);//插入的元素个数
while (x > 0) {
CLNode* s = (CLNode*)malloc(sizeof(CLNode));//创建插入节点
scanf("%d", &s->data);//赋值
s->next = L->next;
L->next = s;//头结点连接s
x--;
}
return L;
}
//尾插法
CLinkList CList_TailInsert(CLinkList& L) {
L = (CLNode*)malloc(sizeof(CLNode));//创建头结点
L->next = L;
int x;
scanf("%d", &x);//插入的元素个数
CLNode* p = L;
while (x > 0) {
CLNode* s = (CLNode*)malloc(sizeof(CLNode));//创建插入节点
scanf("%d", &s->data);//赋值
s->next = L;//最后一个节点指向头结点
p->next = s;//尾结点连接插入节点
p = s;//将插入节点作为尾结点
x--;
}
return L;
}
//按位序插入:在第i个位置插入元素
bool CListInsert(CLinkList& L, int i, ElemType e) {
if (i < 1) {//i值不合法
return false;
}
CLNode* p = L;//指针p指向当前扫描到的节点
while (i > 1) {//找到要插入节点的前一个位置
p = p->next;
i--;
if (p == L) {
return false;
}
}
CLNode* s = (CLNode*)malloc(sizeof(CLNode));
s->data = e;
s->next = p->next;
p->next = s;//将节点s连接到p之后
return true;
}
//指定节点的后插操作:在p节点之后插入元素e
bool InsertNextCNode(CLNode* p, ElemType e) {
if (p == NULL)//如果指针p为空,无法插入
return false;
CLNode* s = (CLNode*)malloc(sizeof(CLNode));
if (s == NULL)//如果创建插入节点失败,返回错误
return false;
s->data = e;//赋值
s->next = p->next;//将s的后继连接p的后继
p->next = s;//p的后继连接s
return true;
}
//指定节点的前插操作:在p节点之前插入元素e
bool InsertPriorNode(CLNode* p, ElemType e) {
if (p == NULL)//如果指针p为空,无法插入
return false;
CLNode* s = (CLNode*)malloc(sizeof(CLNode));//创建插入节点
if (s == NULL)//创建失败,返回错误
return false;
s->data = p->data;//将p节点的值赋给插入节点,将其作为后插节点即可
p->data = e;//将插入的值赋给p节点
s->next = p->next;
p->next = s;
return true;
}
//按位序删除。删除表L中第i个位置的元素, 并用e返回删除元素的值
bool ListCDelete(CLinkList L, int i, ElemType& e) {
if (i < 1)//删除位置错误
return false;
CLNode* p = L;
while (i != 1) {//找到删除节点的前一个位置
p = p->next;
i--;
if (p->next == L) {
break;
}
}
if (p->next == L)//如果前一个节点遍历到最后一个节点,则删除位置错误
return false;
CLNode* s = p->next;//s指向删除节点
p->next = s->next;//p的后继连接s的后继
e = s->data;//获取删除元素值
free(s);//删除节点
return true;
}
//指定节点的删除
bool DeleteCNode(CLNode* p) {
if (p == NULL) {//如果要删除节点为空,不用删除
return false;
}
ElemType e = p->data;
p->data = p->next->data;//将后一个节点的值赋给要删除的节点
CLNode* s = p->next;//删除下一个节点即可
p->next = s->next;//将当前节点的后继连接给下下个节点
free(s);//删除s节点
return true;
}
//按序号查找节点值
CLNode* GetElem(CLinkList L, int i) {
if (i < 0)
return NULL;
if (i == 0)
return L;
CLNode* p = L->next;
while (p != L && i > 0) {
p = p->next;
i--;
}
if (p == L)
return NULL;
return p;
}
//按值查找表结点
CLNode* LocateElem(CLinkList L, ElemType e) {
CLNode* p = L->next;
while (p!=L && p->data != e) {
p = p->next;
}
if (p == L) {
return NULL;
}
return p;
}
//求表长
int Length(CLinkList L) {
int len = 0;
CLNode* p = L->next;
while (p != L) {
p = p->next;
len++;
}
return len;
}
//判空操作
bool Empty(CLinkList L) {
return L->next == L;
}
//输出操作
void PrintCList(CLinkList L) {
CLNode* p = L->next;
while (p != L) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
//销毁操作
void DestroyCList(CLinkList& L) {
CLNode* p = L->next;
while (p != L) {
CLNode* s = p;
p = p->next;
free(s);
}
free(L);
L = NULL;
}
int main()
{
CLinkList L;
InitCList(L);
cout << Empty(L) << endl;
CList_HeadInsert(L);
cout << Empty(L) << endl;
PrintCList(L);
cout << Length(L) << endl;
DestroyCList(L);
return 0;
}
五、循环双链表
#pragma warning(disable : 4996)
typedef int ElemType;
#include <iostream>
using namespace std;
typedef struct CDNode {
ElemType data;
struct CDNode* prior, * next;
}CDNode, * CDLinkList;
bool InitCDLinkList(CDLinkList& L) {
L = (CDNode*)malloc(sizeof(CDNode));//分配一个头结点
if (L == NULL)//内存不足,分配失败
return false;
L->prior = L->next = L;
return true;
}
//循环双链表的插入
bool InsertNextCDNode(CDNode* p, CDNode* s) {
if (p == NULL || s == NULL) {
return false;
}
s->next = p->next;//将s节点插入到p的后面
p->next->prior = s;
p->next = s;
s->prior = p;
return true;
}
//循环双链表的删除
bool DeleteNextCDNode(CDNode* p) {
if (p == NULL || p->prior == p&&p->next==p) {//p不能为空,且不能是头结点
return false;
}
CDNode* s = p;
p->prior->next = p->next;
p->next->prior = p->prior;
free(p);
return true;
}
//循环双链表的头插法
CDLinkList DList_HeadInsert(CDLinkList& L) {
InitCDLinkList(L);
int x;
scanf("%d", &x);
while (x > 0) {
CDNode* s = (CDNode*)malloc(sizeof(CDNode));
s->next = s->prior = s;
scanf("%d", &s->data);
InsertNextCDNode(L, s);
x--;
}
return L;
}
//双链表的尾插法
CDLinkList CDList_TailInsert(CDLinkList& L) {
InitCDLinkList(L);
int x;
scanf("%d", &x);
CDNode* p = L;
while (x > 0) {
CDNode* s = (CDNode*)malloc(sizeof(CDNode));
s->next = s->prior = s;
scanf("%d", &s->data);
InsertNextCDNode(p, s);
p = s;
x--;
}
return L;
}
//双链表的销毁
void DestoryCDList(CDLinkList& L) {
while (L->next != L) {
DeleteNextCDNode(L->next);
}
free(L);
L = NULL;
}
//打印
void PrintCDList(CDLinkList& L) {
if (L == NULL) {
printf("循环双链表不存在");
return;
}
CDNode* p = L->next;
while (p != L) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
//置空
bool ClearCDList(CDLinkList L) {
while (L->next != L) {
DeleteNextCDNode(L->next);
}
return true;
}
//判空
bool Empty(CDLinkList L) {
return L->next == L;
}
//按值查找
CDNode* LocateElem(CDLinkList L, ElemType e) {
CDNode* s = L->next;
while (s != L && s->data != e) {
s = s->next;
}
if (s == L) {
return NULL;
}
return s;
}
//按位查找
CDNode* GetElem(CDLinkList L, int i) {
if (i < 0) {
return NULL;
}
if (i == 0) {
return L;
}
CDNode* s = L->next;
while (s != L && i > 1) {
i--;
s = s->next;
}
if (s == L)
return NULL;
return s;
}
int main() {
CDLinkList L;
InitCDLinkList(L);
cout << Empty(L) << endl;
CDList_TailInsert(L);
PrintCDList(L);
cout << GetElem(L, 2)->data << endl;
ClearCDList(L);
cout << Empty(L);
//DeleteNextCDNode(L->next->next);
PrintCDList(L);
DestoryCDList(L);
PrintCDList(L);
return 0;
}
六、静态链表
#pragma warning(disable : 4996)
#define MaxSize 100
typedef int ElemType;
#include <iostream>
#include<stdlib.h>
using namespace std;
typedef struct SLNode {
ElemType data;
int next;
}SLinkList[MaxSize];
//初始化
bool InitSList(SLinkList L) {
L[0].next = -1;//-1代表结束
for (int i = 0; i < MaxSize; i++) {
L[i].data = INT_MIN;//INT_MIN代表数据为空
}
return true;
}
//头插法
void SList_HeadInsert(SLinkList L) {
int x;
scanf("%d", &x);//插入节点个数
InitSList(L);
for (int i = 1; i < MaxSize && x>0; i++) {
if (L[i].data == INT_MIN) {//如果有空节点,赋值插入
scanf("%d", &L[i].data);
L[i].next = L[0].next;
L[0].next = i;
x--;
}
}
}
//尾插法
void SList_TailInsert(SLinkList L) {
int x;
scanf("%d", &x);//插入节点个数
InitSList(L);
for (int i = 1, j = 0; i < MaxSize && x>0; i++) {
if (L[i].data == INT_MIN) {//如果有空节点,赋值插入
scanf("%d", &L[i].data);
L[i].next = L[j].next;
L[j].next = i;
j = i;
x--;
}
}
}
//打印
void PrintSList(SLinkList L) {
int i = L[0].next;
while (i != -1) {
printf("%d ", L[i].data);
i = L[i].next;
}
printf("\n");
}
//表长
int Length(SLinkList L) {
int len = 0;
int i = 0;
while (L[i].next != -1) {
i = L[i].next;
len++;
}
return len;
}
//按值查找
int LocateElem(SLinkList L, ElemType e) {
int i = L[0].next;
while (i != -1 && L[i].data != e) {
i = L[i].next;
}
return i;//找到返回下标,找不到返回-1
}
//按位查找:返回的是下标
int GetElem(SLinkList L, int i) {
int k = L[0].next;
if (i < 0) {//i不合理
return -1;
}
if (i == 0) {//i为头结点
return 0;
}
while (k != -1&&i > 1) {//查找
k = L[k].next;
i--;
}
return k;//找到返回下标,找不到返回-1
}
//插入
bool SListInsert(SLinkList L, int i, ElemType e) {
if (i <= 0) {//插入位置错误
return false;
}
int x = 0;
while (x != -1&&i > 1) {//寻找前一个位置
x = L[x].next;
i--;
}
if (x == -1) {//插入位置错误
return false;
}
int k = 1;
while(k < MaxSize && L[k].data != INT_MIN)
k++;//寻找未使用的空间
if (k == MaxSize) {//无可用空间
return false;
}
L[k].data = e;//赋值
L[k].next = L[x].next;//插入
L[x].next = k;
return true;
}
//删除
bool SListDelete(SLinkList L, int i, ElemType &e) {
int x = 0;
if (i <= 0) {//删除位置错误
return false;
}
while (x != -1&& i > 1) {//寻找前一个位置
x = L[x].next;
i--;
}
if (x == -1 || L[x].next == -1) {//删除位置错误
return false;
}
e = L[L[x].next].data;//获取删除节点值
L[L[x].next].data = INT_MIN;//将删除节点置空
L[x].next = L[L[x].next].next;//将删除节点的前驱连接后继
return true;
}
//判空
bool Empty(SLinkList L) {
return L[0].next == -1;
}
int main(){
SLinkList L;
SList_TailInsert(L);
SListInsert(L, 6, 6);
ElemType e;
SListDelete(L, 6, e);
printf("e=%d\n", e);
PrintSList(L);
printf("len=%d\n", Length(L));
printf("i=2 : %d\n", L[GetElem(L, 2)].data);
printf("e=2 : %d\n", LocateElem(L, 2));
}
七、提升
(一)单链表相关
//提升
//在不带头结点的单链表中,删除所有值为x的节点,有返回值递归
//算法思想:相当于在回溯时,前一个指针指向回溯得到的节点,当遇到x节点,则移向下一个,删除当前节点,最终返回不等于x的节点
LNode* DeleteNodeX(LinkList L, ElemType x) {
if (L == NULL) {
return NULL;
}
L->next = DeleteNodeX(L->next, x);
if (L->data == x) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s = L;
L = L->next;
free(s);
return L;
}
return L;
}
//在不带头结点的单链表中,删除所有值为x的节点,无返回值递归
//算法思想:同上,&L引用思想,上一指针存入L->next,则下一指针L=L->next<=>L->next=L->next->next,同样完成了对x的删除操作
void DeleteNodeX1(LinkList& L, ElemType x) {
if (L == NULL) {
return;
}
if (L->data == x) {
LNode* s = L;
L = L->next;
free(s);
DeleteNodeX1(L, x);
}else
DeleteNodeX1(L->next, x);
}
void DeleteNodeX3(LNode** head, int val) {
if (*head == NULL) {
return;
}
if ((*head)->data == val) {
LNode* s = *head;
*head = (*head)->next;
free(s);
DeleteNodeX3(head, val);
}
else {
DeleteNodeX3(&(*head)->next, val);
}
}
//在带头节点的单链表,删除所有值为x的节点
//算法思想:迭代,当遇到x是,将他的前驱指向其后继
void DeleteNodeX2(LinkList &L, ElemType x) {
LNode* p = L;
while (p->next) {
LNode* s = p->next;
if (s->data == x) {
p->next = s->next;
free(s);
}
else {
p = p->next;
}
}
}
//反向输出带头结点单链表的每个节点值
//算法思想:回溯算法,递归到最后,然后从尾到头回溯输出
void R_Print(LinkList L) {
if (L->next == NULL) {
return;
}
R_Print(L->next);
printf("%d ", L->next->data);
}
//删除带头节点单链表中最小节点
//算法思想:先找到最小节点的前驱节点,每次与最小值比较,当比当前最小值小,记录其前驱,并更改当前最小值
void Del_Min_Node(LinkList L) {
ElemType min_data;
if (L->next != NULL) {
min_data = L->next->data;
}
LNode* p = NULL;
while (L->next != NULL) {
if (L->next->data <= min_data) {
min_data = L->next->data;
p = L;
}
L = L->next;
}
if (p != NULL) {
LNode* s = p->next;
p->next = s->next;
free(s);
}
}
//就地逆置带头结点单链表,空间复杂度为O(1)
//算法思想:方法一:利用头插法思路,将每个节点插入到头结点之后
void reverse_1(LinkList& L) {
LNode* p = L->next;
L->next = NULL;
while (p) {
LNode* s = p;
p = p->next;
s->next = L->next;
L->next = s;
}
}
//算法思想:方法二:除第一个节点指向空外,每个节点都指向其前驱节点,在将头结点连接尾结点
void reverse_2(LinkList& L) {
LNode* p = L->next;
if (p == NULL) {
return;
}
LNode* s = p->next;
p->next = NULL;
while (s) {
LNode* n = s->next;
s->next = p;
p = s;
s = n;
}
L->next = p;
}
//将带头节点单链表递增有序
//算法思想:插入排序思想,从第一个节点开始,
void Sort_LinkList1(LinkList& L) {
LNode* p = L->next;
L->next = NULL;
while (p) {
LNode* s = L,*c = p;
while (s->next != NULL&&c->data > s->next->data) {
s = s->next;
}
p = p->next;
c->next = s->next;
s->next = c;
}
}
//删除带头节点无序单链表节点值介于min~max的节点
//算法思想,当找到符合条件的节点将前驱连接其后继,删除当前节点,如果不符合条件的,寻找下一个
void RangeDelete(LinkList& L,ElemType min, ElemType max) {
LNode* p = L;
while (p->next) {
LNode* s = p->next;
if (s->data >= min && s->data <= max) {
p->next = s->next;
free(s);
}
else {
p = p->next;
}
}
}
//找到两个单链表的公共节点
//算法思想:方法一:双指针,head1->a->b->c->d->e->f
// ^
// |
// head2->h->i
//指针a指向head1,指针b指向head2,同时遍历,当a到达末尾时,指向head2,,b到达末尾时,指向head1,直到两者相等停止
//a移动的距离ab+cf+hc,b移动距离hc+cf+ac,两者相等,如果两者不相交指向NULL
//其他方法:https://blog.csdn.net/weixin_44529350/article/details/98726838
LNode* Search_1st_Common(LinkList L1, LinkList L2) {
LNode* a = L1, * b = L2;
while (a != b) {
a = a == NULL ? L1 : a->next;
b = b == NULL ? L2 : b->next;
}
return a;
}
//按递增次序输出无序带头节点单链表中个结点的数据,并删除节点
//算法思想:每次寻找要删除的最小节点找到后输出并删除,如此反复,直到链表为空
void Min_Delete(LinkList& L) {
while(L->next) {
int min = L->next->data;
LNode* p = L;
LNode* s = L;
while (p->next) {
if (min >= p->next->data) {
s = p;
min = p->next->data;
}
p = p->next;
}
printf("%d ", min);
p = s->next;
s->next = p->next;
free(p);
}
printf("\n");
}
//将一个带头结点的单链表A,分成链表A和B,A保存序号为奇数的节点,B保存序号为偶数的节点
//算法思想:用i来判断是奇数还是偶数,奇数节点不变,移向下一个,如果是偶数,将当前节点的前驱连接其后继,当前节点后插入B中
LinkList DisCreat_1(LinkList &A){
int i = 1;
LNode* a = A;
LinkList B = (LinkList)malloc(sizeof(LNode));
B->next = NULL;
LNode* b = B;
while (a->next) {
if (i % 2 == 1) {
a = a->next;
}
else {
b->next = a->next;
a->next = a->next->next;
b->next->next = NULL;
b = b->next;
}
i++;
}
return B;
}
//将一个带头结点的单链表A{a1,b1,a2,b2,...,an,bn},分成链表A{a1,a2,..,an}和B{bn,...,b2,b1}
//算法思想:用i来判断是奇数还是偶数,奇数节点不变,移向下一个,如果是偶数,将当前节点的前驱连接其后继,当前节点前插插入B中
LinkList DisCreat_2(LinkList& A) {
int i = 1;
LNode* a = A;
LinkList B = (LinkList)malloc(sizeof(LNode));
B->next = NULL;
LNode* b = B;
while (a->next) {
if (i % 2 == 1) {
a = a->next;
}
else {
LNode* s = a->next;
a->next = s->next;
s->next = b->next;
b->next = s;
}
i++;
}
return B;
}
//删除有序链表中的重复元素
//算法思想:与前一个元素比较,不同
void Del_Same(LinkList& L) {
LNode* p = L->next;
while (p&&p->next) {
if (p->data == p->next->data) {
LNode* s = p->next;
p->next = s->next;
free(s);
}
else {
p = p->next;
}
}
}
//将两个递增有序单链表La,Lb,合并成一个有序递减单链表,La作为头结点,不开辟新的存储空间
//算法思想:遍历La和Lb,比较当前两个节点值,使用前插法,小的先插入
void MergeList(LinkList& La, LinkList& Lb) {
LNode* pa = La->next, * pb = Lb->next;
La->next = NULL;
while (pa || pb) {
if (pa!=NULL&&pb==NULL||pa!=NULL&&pb!=NULL&&pa->data < pb->data) {
LNode* sa = pa;
pa = pa->next;
sa->next = La->next;
La->next = sa;
}
else {
LNode* sb = pb;
pb = pb->next;
sb->next = La->next;
La->next = sb;
}
}
free(Lb);
}
//设两个单链表A和B,其中元素递增有序.设计一个算法从A和B中的公共元素产生单链表C,要求不破坏A、B节点
//算法思想:两两比较,小的后移,相等在链表C进行传值后插操作
LinkList Get_Common(LinkList A, LinkList B) {
LNode* pa = A->next, * pb = B->next;
LinkList C = (LNode*)malloc(sizeof(LNode));
C->next = NULL;
LNode* pc = C;
while (pa && pb) {
if (pa->data == pb->data) {
pc->next = (LNode*)malloc(sizeof(LNode));
pc = pc->next;
pc->data = pa->data;
pc->next = NULL;
pa = pa->next;
pb = pb->next;
}
else if (pa->data < pb->data) {
pa = pa->next;
}
else {
pb = pb->next;
}
}
return C;
}
//求两个有序链表A,B的交集,其他元素删除,不开辟新空间,用A作为返回的链表
//算法思想:两两比较,小的后移,相等在链表A进行后插操作,如果两个链表当前节点值不相等删除遍历过的节点,相等则将a节点插入,b节点删除
LinkList Union1(LinkList A, LinkList B) {
LNode* pa = A->next, * pb = B->next;
A->next = NULL;
LNode* pc = A;
while (pa && pb) {
LNode* sa = pa;
LNode* sb = pb;
if (pa->data == pb->data) {
pa = pa->next;
pb = pb->next;
free(sb);
sa->next = NULL;
pc->next = sa;
pc = sa;
}
else if (pa->data < pb->data) {
pa = pa->next;
free(sa);
}
else {
pb = pb->next;
free(sb);
}
}
free(B);
return A;
}
//两个整数序列A和B存入两个单链表,判断B序列是否是A的连续子序列
//算法思想:重两个链表第一个元素开始,相等则同时后移,不等A链表的指针指向上次开始遍历的节点的后继,B链表的指针指向第一个元素
bool Pattern(LinkList A, LinkList B) {
LNode *a = A->next, *b = B->next;
while (a) {
LNode* sa = a;
LNode* sb = b;
while (sa!=NULL&&sb!=NULL&&sa->data == sb->data) {
sa = sa->next;
sb = sb->next;
}
if (sb == NULL) {
return true;
}
a = a->next;
}
return false;
}
//查找单链表倒数第k个节点:一次遍历
//算法思想:定义两个指针都指向第一个元素,让一个节点先走k个节点,如果遍历到最后说明不满足k个节点,返回false,否则,利用与另一个节点的长度差,同时移动,当一个节点遍历到最后,输出另一个节点即可
int Search_K(LinkList list, int k) {
LNode* p1 = list->next, *p2 = p1;
while (p1 != NULL && k--) {//遍历到第k个节点
p1 = p1->next;
}
if (p1 == NULL) {
return false;
}
while (p1 != NULL) {
p1 = p1->next;
p2 = p2->next;
}
printf("%d\n", p2->data);
return 1;
}
//单链表保存m个整数,且|data|<=n,对于链表data的局对峙相等的节点仅仅保留第一次出现的节点二删除其余绝对值相等的节点
//算法思想:用一个数组,初始化为0,如果为0代表未访问,保存节点,当前值做数组下标标记为1,指针后移一位,如果为1代表访问过,删除当前节点
void Del_Abs_Same_Node(LinkList L, int n) {
int* hash = (int*)malloc(sizeof(int) * (n+1));
for (int i = 0; i <= n; i++) {
hash[i] = 0;
}
while (L->next) {
int k = L->next->data > 0 ? L->next->data : -L->next->data;
if (hash[k] == 0) {
hash[k]++;
L = L->next;
}
else {
LNode* s = L->next;
L->next = s->next;
free(s);
}
}
free(hash);
}
//线性表L={a1,a2,a3,...,an-2,an-1,an},变成L={a1,an,a2,an-1,a3,an-2,...}
//算法思想: 1.找中间节点 2.分开前后部分,逆置后半部分 3.将后半部分插入到前半部分
void Change_List(LNode *(&L)) {
LNode* p1 = L,*p2 = L;
//例:1->2->3->4->5->null
//p1 = 1;p2 = 1;
//找到中间位置,利用快慢指针
while (p2!=NULL&&p2->next!=NULL) {
p1 = p1->next;
p2 = p2->next->next;
}//例:p1 = 3;p2=null
p2 = p1->next;//p2指向后半段第一个元素
p1->next = NULL;
//例:p2 = 4
//逆置中间位置以后的节点,前插法
while (p2) {
LNode* s = p2;
p2 = p2->next;
s->next = p1->next;
p1->next = s;
}//1->2->3->5->4->null p1 = 3 p2 = null;
p2 = p1->next;//p2 = 5
p1->next = NULL;//1->2->3->null 5->4->null
p1 = L->next;//p1 = 1
//将后半段插入到前半段
while (p2) {
LNode* s = p2;
p2 = p2->next;
s->next = p1->next;
p1->next = s;
p1 = p1->next->next;
}//p1 = 3 p2 =null
}
//判断链表是否有环,并找到进入环的节点返回
//算法思想:快慢指针,
LNode* FindLoopStart(LNode* head) {
LNode* slow = head, * fast = head;
while (fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast) {
break;
}
}
if (fast == NULL || fast->next == NULL) {
return NULL;
}
fast = head;
while (slow != fast) {
slow = slow->next;
fast = fast->next;
}
return slow;
}
int main()
{
LinkList L,A,B;
InitList(A);
InitList(B);
//cout << "empty:" <<Empty(L) << endl;
List_TailInsert(A);
//List_TailInsert(B);
//cout << "empty:" << Empty(L) << endl;
//Del_Min_Node(L);
cout << "打印数组" << endl;
PrintList(A);
//PrintList(B);
//cout << endl <<"len:"<<Length(L) << endl;
//DeleteNodeX1(L, 3);
//DeleteNodeX2(L, 3);
//DeleteNodeX3(&L, 3);
//reverse_2(L);
//Sort_LinkList1(L);
//RangeDelete(L, 1, 3);
//Min_Delete(L);
//MergeList(A,B);
//L = Get_Common(A, B);
//L=Union1(A, B);
//cout << "Pattern(A,B):" <<Pattern(A, B) << endl;
//cout << "Setach(A,k)" << Search_K(A, 3) << endl;
//Del_Abs_Same_Node(A, 100);
LNode* s = A;
while (s->next) {
s = s->next;
}
//s->next = A->next;
//Change_List(A);
A=FindLoopStart(A);
cout << "打印数组" << endl;
cout << "A->data:"<<A << endl;
//PrintList(A);
//cout << "len:" << Length(L) << endl;
//DestroyList(L);
}
(二)循环双链表相关
//提升
//判断循环双链表是否对称
//定义两个指针一个指向链表末尾,一个指向链表开头
bool isSymmetry(CDLinkList L) {
CDNode* last = L->prior, * start = L->next;
while (last != start) {
if (last->data != start->data) {
return false;
}
last = last->prior;
start = start->next;
}
return true;
}
(三)循环单链表相关
//带头节点循环单链表h1和h2,将h2连接到h1后面
//找到h1和h2的最后位置,h1最后元素连接h2第一个元素,h2最后一个元素连接h1头节点
void connect_link(CLinkList &h1, CLinkList &h2) {
CLNode* p1 = h1, *p2 = h2;
while (p1->next != h1) {
p1 = p1->next;
}
while (p2->next != h2) {
p2 = p2->next;
}
p2->next = h1;
p1->next = h2->next;
free(h2);
}
//每次从带头节点循环单链表中找最小元素,输出并删除,知道只剩头节点为止,删除头节点
//每次从第一个元素开始找最小元素,一一比较,记录当前最小值。直到遍历到末尾,输出最小值并删除元素,循环执行以上步骤,知道链表为空
void find_min(CLinkList& L) {
CLNode* p = L;
while (p->next != L) {
CLNode* s = p;
int min = s->next->data;
while (p->next != L) {
if (p->next->data < min) {
s = p;
min = s->next->data;
}
p = p->next;
}
CLNode* del = s->next;
s->next = s->next->next;
printf("%d ", del->data);
free(del);
p = L;
}
printf("\n");
free(L);
}
(四)双链表相关
typedef struct DNode {
ElemType data;
struct DNode* prior, * next;
int freq;
}DNode, * DLinkList;
bool InitDLinkList(DLinkList& L) {
L = (DNode*)malloc(sizeof(DNode));//分配一个头结点
if (L == NULL)//内存不足,分配失败
return false;
L->prior = L->next = NULL;
L->freq = 0;
return true;
}
//双链表的插入
bool InsertNextDNode(DNode* p, DNode* s) {
if (p == NULL || s == NULL) {
return false;
}
s->next = p->next;//将s节点插入到p的后面
if (p->next != NULL) {
p->next->prior = s;
}
p->next = s;
s->prior = p;
return true;
}
//双链表的删除
bool DeleteNextDNode(DNode* p) {
if (p == NULL || p->prior == NULL) {//p不能为空,且不能是头结点
return false;
}
DNode* s = p;
p->prior->next = p->next;
if (p->next != NULL)
p->next->prior = p->prior;
free(p);
return true;
}
//双链表的头插法
DLinkList DList_HeadInsert(DLinkList& L) {
InitDLinkList(L);
int x;
scanf("%d", &x);
while (x > 0) {
DNode* s = (DNode*)malloc(sizeof(DNode));
s->next = s->prior = NULL;
scanf("%d", &s->data);
s->freq = 0;
InsertNextDNode(L, s);
x--;
}
return L;
}
//在非循环双链表中添加一个频域freg,节点初始化为0,当被访问过一次则频域+1,将链表按频域高低按从大到小排序,,相同则最近访问的节点放前面
//算法思想:先找值为e的节点,找到后,将节点的前驱和后继相互连接,然后当前节点频域+1,找到第一个当前节点频域小的节点,将当前节点前插,返回当前节点的地址
DNode* Locate(DLinkList& L, ElemType e) {
DNode* p = L->next;
while (p!=L) {
if (p->data == e) {
p->freq++;
p->prior->next = p->next;
if(p->next!=NULL)
p->next->prior = p->prior;
DNode* s = L->next;
while (p->freq < s->freq) {
s = s->next;
}
p->next = s;
p->prior = s->prior;
s->prior->next = p;
s->prior = p;
return p;
}
p = p->next;
}
return NULL;
}