
线性表
逻辑结构
数据元素之间的逻辑关系(抽象)
集合结构

线性结构

一对一的关系
假如说,2的前面是1,那么就把1称为2的前驱
3在2的后面,那么把3称为后继
树型结构

只能有一个父系
子系可以有多个
图形结构

存储结构
数据结构在计算机中的表示(具体)
顺序、链式、索引、散列
索引、散列可以由顺序、链式实现。
顺序存储
随机访问的意思是:可以到任意某一个元素(计算的时间是一样的)

链式存储

如果C后面没有了就变成NULL
逻辑结构和存储结构

顺序存储和链式存储优势劣势
顺序存储优势:
- 实现随机存取
- 每个元素占用最少的空间(假如你是int的数组,那么每个就占用4个字节)
顺序存储劣势:
只能使用整块的存储单元,会产出较多的碎片.
链式存储优势:
充分利用所有存储单元,不会出现碎片现象
链式存储劣势:
- 需要额外的
算法定义
1.有穷-执行一个算法能结束
2.确定-可以确定结果
3.可行-可以执行
4.输入-输入的内容
5.输出-加工输入的内容
时间复杂度
时间复杂度指的是算法中所有语句的频度(执行次数)之和
记为T(n)=O(f(n))
其中n是问题的规模;f(n)是问题规模n的某个函数
假如 输入n个数要排序. 排序算法就要执行n*n那么f(n)=n^2
O(n^2)

数组O(1) 可以拿到任何一个位置的数
时间复杂度是怎么计算的?
eg.1
int sum=0;
sum=n*(n+1)/2;
printf("%d",sum);
因为这里没有for循环. 并且 程序执行了三次. 算法的执行次数等于3
时间复杂度为T(n)=O(1);
表示不会随着n的增长而增长
eg.2
int x=2;
while(x<n/2)
x=2*x;
执行频率最高的就是x=2*x;
初始x=2,下一次4,8,16,32
2^t+1<n/2
那就同取log,根据时间复杂度计算忽略高阶项系数和低阶项.
时间复杂度T(n)=O(

eg.3

最终是n-1 根据时间复杂度计算忽略高阶项系数和低阶项.
T(n)=O(n);
eg.4

首先外层循环,for(i=0;i<n;i++){}
根据n是执行了n次
内层循环while(x<n/2) x=2*x;
1 2 4 8 16 32
2^n+1<n/2
取log 为

再*外层的n就变为了

eg.5

外层循环是n,内层循环是m, n*m=>n^2,根据时间复杂度乘法规则
O(n^2)
eg.6

这里是两个循环没有嵌套,那么就各自执行
T(n)=max(O(n),O(m));
取最大的
eg.7

取最大的,可以忽略5n并且可以忽略3.从而拿到时间复杂度
T(n)=O(n^3);
空间复杂度
S(n)=O(f(n));
空间复杂度O(1)
n个元素数组排序,不开辟额外的空间(随着n的增长而增长的空间叫做额外空间).[有点抽象,没理解]
空间复杂度就是O(1);
线性表
顺序表
什么是线性表
由n个相同类型的元素组成的有序集合.

线性表中元素个数n,称为线性表的长度. 当n=0时,为空表.
ai-1为ai的直接前驱
ai+1为ai的直接后继
特点
- 表中元素个数是有限的
- 表中元素的数据类型都相同,意味着每一个元素占用相同大小的空间
- 表中元素具有逻辑上的顺序性,在序列中各元素排序由其先后顺序
顺序表的代码定义

优点和缺点


最好的插入情况:在表尾插入,时间复杂度为O(1);
最坏的插入情况:在表头插入,时间复杂度为O(n);
平均情况:在插入概率平均的情况下,平均移动元素的次数为n/2,时间复杂度就为O(n);( 根据省略高次项系数。)
insert code
首先在写代码之前要构建思想。
- 判断是否超出数组位置
- 寻找要插入的位置
- 找到以后,位移后面的元素.
- 插入
- 结束,长度+1
#include <stdio.h>
#include <string.h>
#define MaxSize 50
typedef struct
{
int data[MaxSize];
int len;
}sqllist;
bool insert(sqllist *A,int element,int index)
{
if(index<1||index>A->len+1) return 0;//插入位置是否合法
if(A->len>=MaxSize) return 0;//插入的空间是否足够
for(int i=A->len;i>=index;i--)//index就是要插入的第几个位置
{
A->data[i]=A->data[i-1];//往后移动
//假设i=5,进入这里之后就是A[5]=A[4],也就是A的第四个位置的元素,到第五个去了
}
A->data[index-1]=element;//全部移动完了之后,再插入对应元素的位置
A->len++;//线性表长度加1
return 1;
}
int main()
{
sqllist B={{1,2,3,4,5},5};
if(insert(&B,333,5))
{
printf("success\n");
}else
{
printf("fail\n");
}
for (int i =0;i<B.len;i++)
{
printf("%d,",B.data[i]);
}
return 0;
}
delete code
- 判断是否超出、插入是否合法.
- 找到要删除的数
- 把后面的数字往前移
- 删除要删除的数
- 长度减对应数
- 结束
bool del(sqllist *A,int index){
if(index<1||index>A->len+1) return 0;//插入位置是否合法
if(A->len>=MaxSize) return 0;//插入的空间是否足够
int temp=A->data[index-1];
for(int i=index;i<A->len;i++)
{
A->data[i-1]=A->data[i];
}
A->len--;
return 1;
}
#include <stdio.h>
#include <string.h>
#define MaxSize 50
typedef int ElemType;//昂顺序表存储其他类型元素时,可以快速修改
typedef struct
{
ElemType data[MaxSize];
int len;
}sqllist;
bool insert(sqllist *A,int element,int index)
{
if(index<1||index>A->len+1) return 0;//插入位置是否合法
for(int i=A->len;i>=index;i--)//index就是要插入的第几个位置
{
A->data[i]=A->data[i-1];//往后移动
//假设i=5,进入这里之后就是A[5]=A[4],也就是A的第四个位置的元素,到第五个去了
}
A->data[index-1]=element;//全部移动完了之后,再插入对应元素的位置
A->len++;//线性表长度加1
return 1;
}
bool del(sqllist *A,int index){
if(index<1||index>A->len+1) return 0;//插入位置是否合法
if(A->len>=MaxSize) return 0;//插入的空间是否足够
int temp=A->data[index-1];
for(int i=index;i<A->len;i++)
{
A->data[i-1]=A->data[i];
}
A->len--;
return 1;
}
int main()
{
sqllist B={{1,2,3,4,5},5};
if(insert(&B,333,5))
{
printf("success insert\n");
}else
{
printf("fail insert\n");
}
if(del(&B,1))
{
printf("success del\n");
}else
{
printf("fail del\n");
}
for (int i =0;i<B.len;i++)
{
printf("%d,",B.data[i]);
}
return 0;
}
#include <stdio.h>
#define MaxSize 50
typedef int ElemType;
typedef struct
{
ElemType data[MaxSize];
int len;
}SqList;
bool insert(SqList &A,int element,ElemType index)
{
if(index<1||index>A.len) return 0;//插入是否合法
if(A.len>=MaxSize) return 0;//满了,插入不了了
for(int i=A.len;i>=index;i--)
{
A.data[i]=A.data[i-1];
}
A.data[index-1]=element;
A.len++;
return 1;
}
bool del(SqList &A,ElemType index)
{
if(index<1||index>A.len) return 0;//插入是否合法
for(int i=index;i<=A.len;i++)
{
A.data[i-1]=A.data[i];
}
A.len--;
return 1;
}
void print(SqList &A)
{
for(int i=0;i<A.len;i++)
{
printf("%3d",A.data[i]);
}
}
int main()
{
SqList A={{1,2,3,4,5,6},6};
if(insert(A,333,1))
{
printf("success insert\n");
}else
{
printf("fail insert\n");
};
print(A);
return 0;
}
思考

属于
完整的一套操作-顺序表
#include <stdio.h>
#define MaxSize 50
typedef int ElemType;
typedef struct
{
ElemType data[MaxSize];
int len;
}SqList;
bool insert(SqList &A,int element,ElemType index)
{
if(index<1||index>A.len) return 0;//插入是否合法
if(A.len>=MaxSize) return 0;//满了,插入不了了
for(int i=A.len;i>=index;i--)
{
A.data[i]=A.data[i-1];
}
A.data[index-1]=element;
A.len++;
return 1;
}
bool del(SqList &A,ElemType index)
{
if(index<1||index>A.len) return 0;//插入是否合法
for(int i=index;i<=A.len;i++)
{
A.data[i-1]=A.data[i];
}
A.len--;
return 1;
}
void print(SqList &A)
{
for(int i=0;i<A.len;i++)
{
printf("%3d",A.data[i]);
}
}
//查找某个元素的位置
int findelement(SqList &A,ElemType e)
{
for(int i=0;i<=A.len;i++)
{
if(A.data[i]==e)
{
printf("you want find element in =%d\n",i+1);
}
}
return 0;
}
//查找某个index中存在那个元素
int getelement(SqList &A,int index)
{
printf("you want find index in elemtne%d",A.data[index-1]);
return 0;
}
int main()
{
SqList A={{1,2,3,4,5,6},6};
if(insert(A,333,1))
{
printf("success insert\n");
}else
{
printf("fail insert\n");
};
if(del(A,3))
{
printf("success del\n");
}else
{
printf("fail del\n");
}
print(A);
findelement(A,4);
getelement(A,1);
return 0;
}
OJ作业-课时10作业
AC代码:
#include <stdio.h>
#include <iostream>
#define Max 100
typedef int E;
typedef struct
{
E data[Max];
int len;
}SqList;
bool insert(SqList &I,E element,int index)
{
if(index<1||index>I.len+1) return false;
if(I.len>=Max) return false;
for(int i=I.len;index<=i;i--)
{
I.data[i]=I.data[i-1];
}
I.data[index-1]=element;
I.len++;
return true;
}
bool del(SqList &I,E index)
{
if(index<1||index>I.len) return false;
for(int i=index;i<=I.len;i++)
{
I.data[i-1]=I.data[i];
}
I.len--;
return true;
}
void printList(SqList &I)
{
for(int i =0;i<I.len;i++)//这里不能是等于,等于的话就会存在数组越界
{
printf("%3d",I.data[i]);
}
printf("\n");
}
int main()
{
SqList A={{1,2,3},3};
int element,pos;
scanf("%d",&element);
if(insert(A,element,2))
{
printList(A);
}else
{
printf("false\n");
};
scanf("%d",&pos);
if(del(A,pos))
{
printList(A);
}else
{
printf("false\n");
};
return 0;
}
线性表的链式存储
因为顺序表在
- 插入和删除操作移动大量元素.
- 数组的大小不好确定
- 占用一大段连续的存储空间,造成很多碎片
我们用某种线性表需要再特定的场景下使用。
假如说你要读取很多数据的,就可以考虑使用顺序表。
单链表
[data]+[next]

在定义的时候,和顺序表不大一样。
因为在定义结构体的时候,会用到结构体指针。
当我们存储数据的时候,就是需要用到,这么一个结构体来保存数据.
typedef int ElemType;
typedef struct Lnode
{
ElemType data;//数据域
struct Lnode *next;//指针域指向下一个数据
}Lnode,*LinkList;
每一个节点都有next指针. 当出现NULL的时候,就知道到结尾了.

在a1之前还放了一个,叫做头节点(不放值).L叫做头指针

为什么在a1前加一个头结点?因为带头结点的链表是操作简单的。
若链表有头节点,则头指针永远指向头节点,不论链表是否为空(a1/a2/a3都是没有数据的),头指针均不为空,头指针是链表的必须元素,他表示一个链表.
头节点是为了操作的方便而设立的,其数据域一般为空,或者存放链表的长度.有头结点后,对在第一节点前插入和删除第一节点的操作就统一了,不需要频繁重置头指针.但头节点不是必须的.
优点

缺点

指针域不是存数据的。是存指针的。
插入思路
在表头插入时:

q->next=p->next;(指针域)
p->next=q(指针域指向q的数据域)
在表中插入时:

q->next=p->next;
p->next=q;
表尾插入时

p->next=q;(p的指针域指向q的数据域)
q->next=NULL;(q的指针域指向NULL)
删除
在表头/表中/表尾删除时:

q=p->next;(拿到要删除的元素)
p->next=q->next;(p的指针域[表头L]指向q指向的指针域a2)
free(q);
查找
按index查找

按元素查找

代码实战
头插法+中间法
插入
开始->定义链表指针->申请空间->scanf读取第一个元素->while循环建立链表->print->结束
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
typedef int E;
// 头插法+中间插
typedef struct Lnode {
E data;
struct Lnode *next; // 指向下一个节点
} Lnode, *posLnode;
bool insert(posLnode &A) {
A = (posLnode)malloc(sizeof(Lnode)); // 申请头节点的空间
A->next = NULL;
E x; // 定义存放数据
// 先读取一次数据,初始化 x
scanf("%d", &x);
while (x != 9999) {
Lnode *s = (posLnode)malloc(sizeof(Lnode));
s->data = x;
s->next = A->next; // 让新节点 s,指向原来那个节点
A->next = s;
scanf("%d", &x); // 读取下一个数据
}
return true;
}
void print(posLnode A) {
A = A->next; // 跳过头节点
while (A != NULL) {
printf("%3d", A->data);
A = A->next;
}
printf("\n");
}
int main() {
posLnode A; // 链表头指针
insert(A);
print(A);
return 0;
}
尾插法
开始->定义链表头指针L->头节点申请空间,定义尾指针r,指向头结点L->scanf读取第一个元素->开启while循环建链表->结束

#include <iostream>
typedef int E;
typedef struct Lnode
{
E data;
struct Lnode *next;
}Lnode,*p;
void insert_tail(Lnode *&L)
{
//头指针
L=(p)malloc(sizeof(Lnode));//申请节点空间
L->next=NULL;
//尾指针指向L(始终指向链表尾部),存储a(1-n)的s
Lnode *r=L,*s;
//输入
E x;
scanf("%d",&x);
//定义
while (x!=9999)
{
s=(p)malloc(sizeof(Lnode));//为新节点申请空间
s->data=x;
r->next=s;//新节点给尾节点的next指针
r=s;
scanf("%d",&x);
}
r->next=NULL;
}
void print(p A) {
A = A->next; // 跳过头节点
while (A != NULL) {
printf("%3d", A->data);
A = A->next;
}
printf("\n");
}
int main()
{
p L;
insert_tail(L);
print(L);
return 0;
}
按值查找与按位置查找
按值查找
开始->定义链表头指针->尾插法新建链表->查找某个位置的元素值,需要判断是否合法,L是从0开始的->遍历查找index对应的元素值->结束输出
p GetElem(p A,int index)
{
//A起头是0开始的.
int i = 0;
if(index<1) return NULL;
while (i<index)
{
A=A->next;//L指向第一个有数据的
i++;
}
return A;
}
按位置查找
p LocateElem(p A,E element)
{
while (A)
{
if(A->data==element)
{
return A;//如果找到对应的值就返回节点index
}
A=A->next;
}
return NULL;
}
小完整-差删除
#include <iostream>
typedef int E;
typedef struct Lnode
{
E data;
struct Lnode *next;
}Lnode,*p;
void insert_tail(Lnode *&L)
{
//头指针
L=(p)malloc(sizeof(Lnode));//申请节点空间
L->next=NULL;
//尾指针指向L(始终指向链表尾部),存储a(1-n)的s
Lnode *r=L,*s;
//输入
E x;
scanf("%d",&x);
//定义
while (x!=9999)
{
s=(p)malloc(sizeof(Lnode));//为新节点申请空间
s->data=x;
r->next=s;//新节点给尾节点的next指针
r=s;
scanf("%d",&x);
}
r->next=NULL;
}
void insert_head(Lnode *&L)
{
//首先给L开辟一个空间,用来存放头指针
L=(p)malloc(sizeof(Lnode));//申请节点空间
L->next=NULL;
//输入数据
Lnode *s;//定义存放数据的 数据指针/数据域
E x;
scanf("%d",&x);
while (x!=9999)
{
//给s开辟一条存放数据的空间
s=(p)malloc(sizeof(Lnode));
s->data=x;
s->next=L->next;
L->next=s;
scanf("%d",&x);
}
}
void print(p A) {
A = A->next; // 跳过头节点
while (A != NULL) {
printf("%3d", A->data);
A = A->next;
}
printf("\n");
}
p GetElem(p A,int index)
{
//A起头是0开始的.
int i = 0;
if(index<1) return NULL;
while (i<index)
{
A=A->next;//L指向第一个有数据的
i++;
}
return A;
}
p LocateElem(p A,E element)
{
while (A)
{
if(A->data==element)
{
return A;//如果找到对应的值就返回节点index
}
A=A->next;
}
return NULL;
}
int main()
{
p L,GetE,LocE;
// insert_tail(L);
insert_head(L);
print(L);
GetE=GetElem(L,2);
if(GetE)
{
printf("success getelem\n");
printf("%d\n",GetE->data);
}else
{
printf("faild getelem\n");
};
LocE=LocateElem(L,7);
if(LocE)
{
printf("success LocE\n");
printf("%d\n",LocE->data);
}else
{
printf("faild LocE\n");
};
return 0;
}
往第i个位置插入元素
bool insert_i_pos(Lnode *&A,int index,E element)
{
p AAA = GetElem(A, index - 1);
if (AAA == NULL) return false;
p q = (p)malloc(sizeof(Lnode)); //给新节点申请空间
q->data = element;
q->next = AAA->next;
AAA->next = q;
return true;
}
单链表完整代码:
#include <iostream>
typedef int E;
typedef struct Lnode
{
E data;
struct Lnode *next;
}Lnode,*p;
void insert_tail(Lnode *&L)
{
//头指针
L=(p)malloc(sizeof(Lnode));//申请节点空间
L->next=NULL;
//尾指针指向L(始终指向链表尾部),存储a(1-n)的s
Lnode *r=L,*s;
//输入
E x;
scanf("%d",&x);
//定义
while (x!=9999)
{
s=(p)malloc(sizeof(Lnode));//为新节点申请空间
s->data=x;
r->next=s;//新节点给尾节点的next指针
r=s;
scanf("%d",&x);
}
r->next=NULL;
}
void insert_head(Lnode *&L)
{
//首先给L开辟一个空间,用来存放头指针
L=(p)malloc(sizeof(Lnode));//申请节点空间
L->next=NULL;
//输入数据
Lnode *s;//定义存放数据的 数据指针/数据域
E x;
scanf("%d",&x);
while (x!=9999)
{
//给s开辟一条存放数据的空间
s=(p)malloc(sizeof(Lnode));
s->data=x;
s->next=L->next;
L->next=s;
scanf("%d",&x);
}
}
void print(p A) {
A = A->next; // 跳过头节点
while (A != NULL) {
printf("%3d", A->data);
A = A->next;
}
printf("\n");
}
p GetElem(p A,int index)
{
//A起头是0开始的.
int i = 0;
if(index<0) return NULL;
while (i<index)
{
A=A->next;//L指向第一个有数据的
i++;
}
return A;
}
p LocateElem(p A,E element)
{
while (A)
{
if(A->data==element)
{
return A;//如果找到对应的值就返回节点index
}
A=A->next;
}
return NULL;
}
//index代表插入索引
bool insert_i_pos(Lnode *&A,int index,E element)
{
p AAA = GetElem(A, index - 1);
if (AAA == NULL) return false;
p q = (p)malloc(sizeof(Lnode)); //给新节点申请空间
q->data = element;
q->next = AAA->next;
AAA->next = q;
return true;
}
//要删除第几个元素
bool del(Lnode *&A,int index)
{
p q;
p AAA=GetElem(A,index-1);//拿到要删除的元素前一个指针
if(AAA==NULL) return false;
q=AAA->next;//拿到要删除的指针
if(q==NULL) return false;//当链表只有5个节点,删除第6个节点时返回false
AAA->next=q->next;//断链
free(q);
return true;
}
int main()
{
p L,GetE,LocE;
// insert_tail(L);
insert_head(L);
print(L);
/*
GetE=GetElem(L,2);
if(GetE)
{
printf("success getelem\n");
printf("%d\n",GetE->data);
}else
{
printf("faild getelem\n");
};
LocE=LocateElem(L,7);
if(LocE)
{
printf("success LocE\n");
printf("%d\n",LocE->data);
}else
{
printf("faild LocE\n");
};
*/
insert_i_pos(L,2,99);
print(L);
del(L,3);
print(L);
return 0;
}
OJ作业-课时11作业


#include <iostream>
typedef int E;
typedef struct Lnode
{
E data;
struct Lnode *next;
}Lnode,*p;// struct Lnode *p;
void insert_head(p &L)
{
//定义头指针L
L=(p)malloc(sizeof(Lnode));
L->next=NULL;
//定义存数据的节点
Lnode *s;
//定义输入数据的变量
E x;
scanf("%d",&x);
while (x!=9999)
{
s=(p)malloc(sizeof(Lnode));
s->data=x;
s->next=L->next;
L->next=s;
scanf("%d",&x);
}
}//insert head
void print_insert_head(p A)
{
int i=0;
A=A->next;
while (A!=NULL)
{
printf("%3d",A->data);
++i;
A=A->next;
}
printf("\n");
};//my print way
void insert_tail(p &L)
{
//初始化头
L=(p)malloc(sizeof(Lnode));
L->next=NULL;
//初始化数据域
p s,r=L;
E x;
scanf("%d",&x);
while (x!=9999)
{
s=(p)malloc(sizeof(Lnode));
s->data=x;
r->next=s;//r的下一个就是s的数据域
r=s;//r直接变成了s,新的插入之后又是一个新的s
scanf("%d",&x);
}
r->next=NULL;
//在尾插法中,没有正确地设置最后一个节点的 next 指针为 NULL。导致在打印链表时,链表的最后一个节点的 next 指针不是 NULL,可能指向了一些未初始化的内存区域,从而导致无限循环。
}
void PrintList(p L)
{
L = L->next;
while (L != NULL)
{
printf("%d", L->data); //打印当前结点数据
L = L->next; //指向下一个结点
if (L != NULL)
{
printf(" ");
}
}
printf("\n");
}//oj print way
//根据index找元素
p GetEle(p L,int index)
{
int i=0;
if(index<0) return NULL;
while (i<index)
{
L=L->next;
i++;
}
return L;
}
//根据元素找index
p FindEle(p L,E element)
{
while (L)
{
if(L->data==element)
{
return L;
}
L=L->next;
}
return NULL;//如果没有找到就返回NULL;
}
bool del(p L,int index)
{
p A;//定义找到删除前一个
p d;//定义要删除的元素
A=GetEle(L,index-1);//找到要删除的元素的前一个
if (A==NULL) return false;
d=A->next;
if(d==NULL) return false;
A->next=d->next;//断链
free(d);
return true;
}
int main()
{
p A,GetE,FindE;
// insert_head(A);
// PrintList(A);
insert_tail(A);
PrintList(A);
GetE=GetEle(A,2);
if(GetE){printf("success find e:%d\n",GetE->data);}else{printf("fail find e");};
FindE=FindEle(A,7);
if(FindE){printf("success find i:%d\n",FindE->data);}else{printf("fail find i");};
del(A,3);
PrintList(A);
return 0;
}
2019年考研真题分析41题-很懵

问题:
空间复杂度在什么情况下不是O(1)?
比如你在原有基础[]n个元素中,又让其中某一个元素开辟了n个空间,就不是空间复杂度为O(1)的了。
设计思想:
空间复杂度为O(1),不能额外申请空间.
找到链表中间节点.
前一半节点为L,后半部分节点为L2.将链表后半部分的创立一个新节点给予L2,再进行原地逆置.
最后交叉合并.

解题设计
- 找到中间节点,并赋予新节点为L2.
- 原地逆置L2
- 合并
代码
//找中间
void find_middle(p L,p &L2)
{
//第二条链表的头节点
L2=(p)malloc(sizeof(NODE));
//构造两个指针,双指针法遍历
p pcur,ppre;
//开始遍历
pcur=ppre=L->next;//首先指到a1
while (pcur!=NULL)
{
pcur=pcur->next;//先走一步,判断后面是否为空
if(pcur==NULL) break;//判断pcur后面是否为空了
pcur=pcur->next;//在走一部
if(pcur==NULL) break;//为了使得偶数个ppre依然指向中间两个节点的前一个
ppre=ppre->next;
}
//这里没怎么理解,还得画个图
L2->next=ppre->next;//由L2头节点指向后面一半链表
ppre->next=NULL;//前一半链表的最后一个节点,next要为null
}
//逆置
//逆转不需要头指针变的
void reverse(p L2)
{
p r,s,t;
r=L2->next;
if(r==NULL) return;//链表为空,第一个节点a1没数据
s=r->next;
if(s==NULL) return;
t=s->next;
while (t)
{
s->next=r;
r=s;//以下三句是三个指针同时向后
s=t;
t=t->next;
}
//这里也不大懂,可以画图
s->next=r;//最后一步了。
L2->next->next=NULL;//逆置后的链表尾部next要为空 L2原本第一个节点要为NULL
L2->next=s;
}
//合并L,L2
void merge(p L,p L2)
{
p pcur,pp,qq;
pcur=L->next;
pp=pcur->next;
qq=L2->next;
while (qq!=NULL&&pp!=NULL)
{
pcur->next=qq;
qq=qq->next;//指向下一个节点 用来遍历l2链表
pcur=pcur->next;
pcur->next=pp;
pp=pp->next;
pcur=pcur->next;
}
if(pp!=NULL){pcur->next=pp;}
if(qq!=NULL){pcur->next=qq;}
}
分析时间复杂度
void find_middle

因为pcur总共走两次,所以是O(n/2)=O(n)
void reverse
因为他遍历的就是L2的长度,所以时间复杂度就是O(n)
void merge
遍历次数O(n/2)=O(n)
三个函数 实际遍历之后就是1.5n -> O(n)
双链表
https://blog.csdn.net/weixin_44162361/article/details/115819742 参考博客
插入

尾插

bool insert_tail(k &p)
{
init(p);
//定义
k s,tail=p;//尾插法.和单链表差不多
E x;
scanf("%d",&x);
while (x!=9999)
{
s=(k)malloc(sizeof(Lnode));
if(s==NULL) return false;
s->data=x;//给予数据
s->next=NULL;//
s->prev=tail;
tail->next=s;
tail=s;
scanf("%d",&x);
};
return true;
}
头插

bool insert_head(k &p)
{
//初始化
init(p);
k s;
E x;
scanf("%d",&x);
while (x!=9999)
{
s=(k)malloc(sizeof(Lnode));
if(s==NULL) return false;
s->data=x;
if(p->next==NULL)
{
s->next=NULL;
s->prev=p;
p->next=s;
}else
{
s->next=p->next;
p->next->prev=s;
s->prev=p;
p->next=s;
}
scanf("%d",&x);
};
return true;
}
删除
k GetEle(k p,int index)
{
p=p->next;
int i = 1;
if(index<1) return NULL;
while (p&&i<index)
{
p=p->next;
i++;
}
return p;
}
bool delete_pos(k &p,int index)
{
if(index<1||len(p)<index) return false;
k q;//要删除的元素
p=GetEle(p,index-1);//拿到前驱节点
if(p==NULL) return false;
q=p->next;//要删除的元素
p->next=q->next;//q的下一个指向上一个元素
q->next->prev=p;//要删除元素的下一个元素的前驱节点被p赋予
free(q);
return true;
}
求表的长度
int len(k p)
{
p=p->next;
int i = 0;
while(p)
{
p=p->next;
i++;
}
return i;
}
循环 单、双链表
//循环单-插入
//循环双-插入
bool insert_tail(k &p)
{
init(p);
//定义
k s, tail = p; // 尾插法,和单链表差不多
E x;
scanf("%d", &x);
while (x != 9999)
{
s = (k)malloc(sizeof(Lnode));
if (s == NULL) return false;
s->data = x; // 赋予数据
if (tail == p) { // 如果链表为空(插入第一个节点)
s->next = p;
s->prev = p;
p->next = s;
p->prev = s;
} else { // 插入后续节点
s->next = p;
s->prev = tail;
tail->next = s;
p->prev = s;
}
tail = s; // 更新尾节点
scanf("%d", &x);
}
return true;
}