线性表的链式表示
特点:不需要使用地址连续的存储单元,插入删除直接修改指针,不需要移动元素
缺点:失去随机存储的优点
1.单链表的节点定义:
//节点的定义
typedef struct LNode {
int data;
struct LNode* next;
} LNode, *LinkList;
一、头结点和头指针的区别:
头指针:永远指向链表的第一个节点
头结点:带有头结点的链表的第一个结点,结点通常不存储信息
二、引入头结点的优点:
1.由于第一个数据节点的位置被存储在头结点的指针域中,因此链表的第一个位置上操作和其他节点一致,不存在区别。
2.无论链表空还是非空,头指针都指向头结点的非空指针。空表非空表得到统一。
三、链表的判空条件
单链表:带头结点:L->next=NULL; 不带头结点:L=NULL;
双链表:带头结点:L->next=NULL;不带头结点:L=NULL;
循环链表:带头结点:L->next=L; 不带头结点 L=NULL;
循环双链表:带头结点:L->next=L;L->prior=L;(L的前驱和后继都为自己) ;不带头结点L=NULL;
二、带有头结点的单链表基本操作
1.头插法:直接定义新指针进行建立
特点:顺序逆序
时间复杂度 o(1) 若表长为n 时间复杂度 o(n)
LinkList headinsert(LinkList& L) {
LNode* s;
int X;
L = (LNode*)malloc(sizeof(LNode));
L->next = NULL;
printf("请输入第1个数");
scanf("%d", &X);
int i = 1;
while (X != 999999) {
s = (LNode*)malloc(sizeof(LNode));
s->data = X;
s->next = L->next;
L->next = s;
i++;
printf("输出第%d个数", i);
scanf("%d", &X);
}
return L;
}
2.尾插法建立链表:利用尾指针来建立链表
特点:输入顺序于存储顺序一致
时间复杂度 o(1) 插入n个:o(n)
LinkList tail(LinkList& L) {
LNode* s;
LNode* tail = L;
int X;
L = (LNode*)malloc(sizeof(LNode));
int i = 0;
printf("请输入%d个数", i);
scanf("%d", &X);
while (X != 999999) {
s = (LNode*)malloc(sizeof(LNode));
s->data = X;
tail->next = s;
tail = s;
}
tail->next = NULL;
return L;
}
3.按序号查找节点(第i个数字结点)值:用变量j来遍历链表至空
时间复杂度:O(n)
LNode* GetElem(LinkList& L, int i) {
LNode* e = L->next;
int j = 1;
if (i == 0)
return L;
if (i < 1)
return NULL;
while (e != NULL && j < i) {
e = e->next;
j++;
}
return e;
}
4.按值查找结点 判断条件 p!=NULL&&p->data=NULL
时间复杂度:O(n)
LNode* locatElem(LinkList& L, int X) {
LNode* e = L->next;
while (e != NULL && e->data != X)
e = e->next;
return e;
}
5.插入操作
插入操作分为指定结点后插入和在第i个位置插入
在第i个位置插入,首先用按位查找定位i-1个位置,然后插入到i-1指定结点后面
时间复杂度:O(n)
bool insertlist(LinkList& L, int i, int X) {
LNode* s= (LNode*)malloc(sizeof(LNode));
LNode* e = GetElem(L, i-1);
s->data = X;
s->next = e->next;
e->next = s;
return true;
}
指定结点后插:
时间复杂度O(1)
LNode *s;
s=(LNode *)malloc(sizeof(LNode));
s->data=X;
s->next=p->next;
p->next=s;
6.删除结点操作
时间复杂度:O(n)用于寻找第i个结点
bool deletelist(LinkList& L, int i) {
LNode* e = GetElem(L, i-1);
LNode* p = e->next;
e->next = p->next;
free(p);
return true;
}
如为删除指定结点p,通过与后继结点交换数值,来删除后继结点,达到同样效果
q=p->next;
p->data=p->data;
p->next=q->next;
free(q);
7.求表长操作:
从第一个数字结点开始用变量i=1来计数
int lengthlist(Linklist L){
int i=0;
while(L->next!=NULL){
i=i++;
L=L->next;
}
return i;
}
8.设计一个递归算法,删除不带头结点的单链表L中所有值为X的结点
思路:递归函数delx(L,x) (画图理解)
1.递归出口 L=NULL;结点为空
2.递归: 若L.data[i]=X 删除结点 并继续递归delx(L->next,x)
若L.data[i]!=X 递归delx(L->next,x);
bool delx(Linklist &L,x){
if(L==NULL)
return true;
if(L->data==x){
LNode *p;
p=L;
free(p);
delx(L->next,x);
}
if(L->data!=x){
delx(L->next,x);
}
return true;
}
9.删除链表中X的值(带头结点)
思路一 删除X 利用一个前驱i结点删除值为x的结点(算个小断链)
遍历链表若值为X,删除p,p后移,pre的后驱结点改为p,free掉原p结点;
链表值若不为X,pre,p依次后移;
bool del_x_linklist(LinkList& L) {
int val;
cout << "请输入要删除的值" << endl;
cin >> val;
int k = 0;
LNode* pre = L;
LNode* q;
LNode* p = pre->next;
while (p != NULL) {
if (p->data == val) {
q = p;
pre->next = p->next;
p = pre->next;
// p = p->next;
//pre->next = p;
free(q);
}
else
{
pre = p;
p = p->next;
}
}
return true;
}
思路二:记录值不为X的值用尾插法建立链表
如果值为X,不插入,跳过,值为X插入;
//尾插法建立
bool del_X2_LinkList(LinkList& L) {
int val;
cout << "请输入要删除的值" << endl;
cin >> val;
LNode* tail = L;
LNode* s = L->next;
LNode* q;
while (s != NULL) {
if (s->data != val)
{
tail->next = s;
tail = s;
s = s->next;
}
else {
q = s;
s = s->next;
free(q);
}
}
tail->next = NULL;
return true;
}
删除最小及节点(删除问题一般需要保留前驱结点,删除不断链)
思路:遍历整个链表找到最小值结点,设置结点有遍历的前驱pre,遍历结点p
记录最小值结点min,最小值结点前驱
然后删除此结点
bool delminlinklist(LinkList& L) {
LNode* p = L->next;
LNode* pre = L;
LNode* minpre = L;
LNode* min = L->next;
while (p != NULL) {
if (p->data < min->data) {
min = p;
minpre = pre;
}
pre = p;
p = p->next;
}
minpre->next = min->next;
free(min);
return true;
}
类似删除最小值结点:
给定一个单链表,按次递增输出单链表各元素的值,并释放结点
思路:本题就是寻找最小结点并删除,然后遍历N次这个操作(删除需要前驱结点)
bool minupdel(LinkList &L){
LNode *p=L->next;
LNode *pre=L;
LNode *minp=p;
LNode *minpre=pre;
while(L->next!=NULL)
{
p=L->next;
pre=L;
while(p!=NULL){
if(p->data<minp->data)
{minp=p;
minpre=pre;
}
pre=p;
p=p->next;
}
minpre->next=minp->next;
cout<<minp->data<<endl;
free(minp);
}
}
删除结点的值位于两个值之间的结点(删除问题,保留前驱结点)
思路:遍历链表,寻找满足条件的值,
需要结点:前驱结点pre,遍历结点p
//删除链表位于两值之间的值的节点
bool dels_t(LinkList &L, int min, int max) {
initlinklist(L);
LNode* pre = L;
LNode* p = L->next;
while (p != NULL) {
if (p->data > min && p->data < max) {
pre->next = p->next;
//free(p);
p = p->next;
}
else {
pre = p;
p = p->next;
}
}
return true;
}
10.链表逆置问题(断链)
将结点p的后继结点指向前驱结点,并保证不断链设tail指针提前指向p的后继
最后将头结点的指针指向p第一个结点(此时)
//逆置链表
//思路:断链操作
bool relinklist(LinkList& L) {
LNode* pre, *p = L->next, *tail = p->next;
p->next = NULL; //第一个数据节点
while (tail != NULL) {
pre = p;
p = tail;
tail = p->next;
p->next = pre;
}
L->next = p;
return true;
}
11.反向输出每个节点的值(可以采用递归)
void reprint(LinkList &L){
if(L->next!=NULL)
reprint(L->next);
cout<<L->data<<endl;
}
我的错误想法:
void reprint(LinkList &L){
L=L->next;
if(L!=NULL)
reprint(L);
if(L!=NULL)
cout<<L->data<<endl;
}
12.链表升序问题(其实也要断链,保留后继结点)
思路:将链表一分为二,前部分初始只包括第一个数据结点,剩余链表属于另一部分
然后把后部分几点有序插入前部分
bool sqlist(LinkList &L) {
LNode *pre, *p = L->next;
LNode *tail = p->next; //tail设置来防止断链
p->next = NULL;
p = tail;
while (p!= NULL) {
tail = p->next; //tail设置来防止断链
pre = L; //pre为已拍好序的链表头结点
while (pre->next!= NULL && pre->next->data < p->data) {
pre = pre->next;
}
p->next = pre->next;
pre->next = p;
p = tail;
}
return true;
}
13.寻找两个链表的公共结点
思路:两个链表的公共结点即某结点后两链表全部相同,这就要求链表同步遍历
若链表不等长,需要先同步遍历处理,求表长,长链表优先遍历长度差
LNode *samelist(LinkList& A, LinkList& B) {
int a = lengthlist(A);
int b = lengthlist(B);
int pos;
LNode *longlist;
LNode *shortlist;
if (a > b) {
longlist = A;
shortlist = B;
pos = a - b;
}
else {
longlist = B;
shortlist = A;
pos = b - a;
}
longlist = longlist->next;
shortlist = shortlist->next;
while (pos--) {
longlist = longlist->next;
}
while (longlist != NULL) {
if (longlist->data == shortlist->data) {
return longlist;
}
longlist = longlist->next;
shortlist = shortlist->next;
}
return NULL;
}
int lengthlist(LinkList &L) {
int i = 0;
LNode* p = L->next;
while (p != NULL) {
p = p->next;
i++;
}
return i;
}
int lengthlist(LinkList &L) {
int i = 0;
LNode* p = L->next;
while (p != NULL) {
p = p->next;
i++;
}
return i;
}
14.将带头结点的单链表分为两个单链表A和B,A含有原本单链表中奇数数据结点,B含有偶数结点,保持顺序不变
思路:用结点P来循环链表,奇数项保留在A,这就要求断链(P保证不断链)
偶数项保留在B,新建链表都采用尾插法
LNode* dicreat_1(LinkList &A){
int i=0; //判断奇数偶数
LNode* B=(LNode*)malloc(sizeof(LNode));
B->next=NULL; //保留偶数
LNode *p=A->next;
A->next=NULL; //保存奇数
LNode *tailA=A;
LNode *tailB=B;
while(p!=NULL){
i++;
if(i%2==1){
tailA->next=p;
tailA=p;
}
else
{
tailB->next=p;
tailB=p;
}
p=p->next;
}
tailA->next=NULL;
tailB->next=NULL;
return B;
}
类似上题将链表A(a1,b1,a2,b2.......an,bn)输出为两个链表(a1,....an)和(bn....b1)
bn采用头插法 注意P前插法断链 保留结点后驱!
LNode* dicreat_2(LinkList &A){
int i=0; //判断奇数偶数
LNode* B=(LNode*)malloc(sizeof(LNode));
B->next=NULL; //保留偶数
LNode *p=A->next;
A->next=NULL; //保存奇数
LNode *tailA=A;
while(p!=NULL){
i++;
if(i%2==1){
tailA->next=p;
tailA=p;
p=p->next;
}
else
{
LNode *q=p->next;
p->next=B->next;
B->next=p;
p=q;
}
}
tailA->next=NULL;
return B;
}
#include <iostream>
using namespace std;
#include <stdio.h>
#include <malloc.h>
//#define _CRT_SECURE_NO_WARNINGS
//队列
//单链表
//节点的定义
typedef struct LNode {
int data;
struct LNode* next;
} *LinkList;
bool initlinklist(LinkList& L) {
L = (LNode*)malloc(sizeof(LNode));
if (L == NULL)
return false;
L->next = NULL;
return true;
}
//头插法
LinkList headinsert(LinkList& L) {
LNode* s;
int X;
int i = 1;
L = (LNode*)malloc(sizeof(LNode));
L->next = NULL;
cout << "请输入第" << i << "个数" << endl;
cin >> X;
while (X != 999999) {
s = (LNode*)malloc(sizeof(LNode));
s->data = X;
s->next = L->next;
L->next = s;
i++;
cout << "请输入第" << i << "个数" << endl;
cin >> X;
}
return L;
}
//尾插法
LinkList tail(LinkList& L) {
LNode* s=(LNode*)malloc(sizeof(LNode));
int X;
L = (LNode*)malloc(sizeof(LNode));
LNode* tail = L;
int i = 1;
cout << "请输入第" << i << "个数" << endl;
cin >> X;
while (X != 999999) {
s = (LNode*)malloc(sizeof(LNode));
s->data = X;
tail->next = s;
tail = s;
i++;
cout << "请输入第" << i << "个数" << endl;
cin >> X;
}
tail->next = NULL;
return L;
}
//按位查找
LNode* GetElem(LinkList& L, int i) {
LNode* e = L->next;
int j = 1;
if (i == 0)
return L;
if (i < 1)
return NULL;
while (e != NULL && j < i) {
e = e->next;
j++;
}
return e;
}
LNode* locatElem(LinkList& L, int X) {
LNode* e = L->next;
while (e != NULL && e->data != X)
e = e->next;
return e;
}
bool insertlist(LinkList& L, int i, int X) {
LNode* s= (LNode*)malloc(sizeof(LNode));
LNode* e = GetElem(L, i-1);
s->data = X;
s->next = e->next;
e->next = s;
return true;
}
bool deletelist(LinkList& L, int i) {
LNode* e = GetElem(L, i-1);
LNode* p = e->next;
e->next = p->next;
free(p);
return true;
}
bool printlinklist(LinkList& L) {
LNode* p = L->next;
while (p != NULL) {
cout << p->data << endl;
p =p ->next;
}
return true;
}
//删除 链表中值为X的节点
bool del_x_linklist(LinkList& L) {
int val;
cout << "请输入要删除的值" << endl;
cin >> val;
int k = 0;
LNode* pre = L;
LNode* q;
LNode* p = pre->next;
while (p != NULL) {
if (p->data == val) {
q = p;
pre->next = p->next;
p = pre->next;
// p = p->next;
//pre->next = p;
free(q);
}
else
{
pre = p;
p = p->next;
}
}
return true;
}
//尾插法建立
bool del_X2_LinkList(LinkList& L) {
int val;
cout << "请输入要删除的值" << endl;
cin >> val;
LNode* tail = L;
LNode* s = L->next;
LNode* q;
while (s != NULL) {
if (s->data != val)
{
tail->next = s;
tail = s;
s = s->next;
}
else {
q = s;
s = s->next;
free(q);
}
}
tail->next = NULL;
return true;
}
//反向输出链表节点的值
//思路:采用递归思想输出
void reprintlist(LinkList &L){
LinkList p = L->next;
if (p->next != NULL)
reprintlist(p);
if (p!= NULL)
cout << p->data << endl;
}
//逆置链表
//思路:断链操作
bool relinklist(LinkList &L) {
LNode* pre, *p= L->next, *tail = p->next;
p->next = NULL; //第一个数据节点
while (tail != NULL) {
pre = p;
p = tail;
tail = p->next;
p->next = pre;
}
L->next = p;
return true;
}
//将一个带有头结点的单链表有序增加
bool sqlist(LinkList &L) {
LNode *pre, *p = L->next;
LNode *tail = p->next;
p->next = NULL;
p = tail;
while (p!= NULL) {
tail = p->next;
pre = L;
while (pre->next!= NULL && pre->next->data < p->data) {
pre = pre->next;
}
p->next = pre->next;
pre->next = p;
p = tail;
}
return true;
}
//最小值删除
bool delminlinklist(LinkList& L) {
LNode* p = L->next;
LNode* pre = L;
LNode* minpre = L;
LNode* min = L->next;
while (p != NULL) {
if (p->data < min->data) {
min = p;
minpre = pre;
}
pre = p;
p = p->next;
}
minpre->next = min->next;
free(min);
return true;
}
//删除链表位于两值之间的值的节点
bool dels_t(LinkList &L, int min, int max) {
initlinklist(L);
LNode* pre = L;
LNode* p = L->next;
while (p != NULL) {
if (p->data > min && p->data < max) {
pre->next = p->next;
//free(p);
p = p->next;
}
else {
pre = p;
p = p->next;
}
}
return true;
}
int lengthlist(LinkList &L) {
int i = 0;
LNode* p = L->next;
while (p != NULL) {
p = p->next;
i++;
}
return i;
}
//寻找两个节点的公共节点
LNode *samelist(LinkList& A, LinkList& B) {
int a = lengthlist(A);
int b = lengthlist(B);
int pos;
LNode *longlist;
LNode *shortlist;
if (a > b) {
longlist = A;
shortlist = B;
pos = a - b;
}
else {
longlist = B;
shortlist = A;
pos = b - a;
}
longlist = longlist->next;
shortlist = shortlist->next;
while (pos--) {
longlist = longlist->next;
}
while (longlist != NULL) {
if (longlist->data == shortlist->data) {
return longlist;
}
longlist = longlist->next;
shortlist = shortlist->next;
}
return NULL;
}
int main( ) {
LinkList L;
LinkList A, B;
LNode *same;
initlinklist(L);
initlinklist(A);
initlinklist(B);
//initlinklist(Same);
cout <<" 请输入A链表的值 "<< endl;
tail(A);
cout << "请输入B链表的数值 "<< endl;
tail(B);
//headinsert(L);
//tail(L);
//reprintlist(L);
//insertlist(L, 1, 3);
//printlinklist(L);
//del_x_linklist(L);
//relinklist(L);
//sqlist(L);
//delminlinklist(L);
same=samelist(A, B);
cout << same->data << endl;
cout << "成功了" << endl;
printlinklist(same);
//dels_t(L, 10, 100);
//printlinklist(L);
//dels_t(L,10,100);
//del_X2_LinkList(L);
//printlinklist(L);
//insertlist(L, 2, 4);
//printlinklist(L);
return 0;
}