二、线性表

本文详细介绍了线性表的定义、基本操作,包括初始化、销毁、插入、删除、查找等,并探讨了顺序存储(顺序表)和链式存储(链表)两种实现方式。顺序表提供了随机访问的优势,但扩展困难,而链表虽然不支持随机访问,但在扩展和插入删除操作上有优势。此外,文章还涵盖了静态链表的概念和特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线性表

一、线性表的定义和基本操作

(一)定义:

线性表是具有相同数据类似的n个元素组成的有限序列,n为表长,当n=0时为空表,用L命名线性表,则表示方式:L=(a1,a2,…,an)。【脚标从1开始
相同数据元素:每个数据元素占有的空间一样大,有利于计算机快速找出具体的元素
有限:有限个。
序列:有次序。

除了第一个元素,其他元素均有唯一一个前驱,除了最后一个元素,其他元素均有唯一一个后继。

(二)基本操作:

1.表的初始化和销毁:

tip:&引用类型的作用:如果参数的修改结果需要带回来,则需要&。【C++内容,C不支持】

1.1.初始化【从无到有】:

InitList(&L):构造一个空的线性表L,分配内存空间

1.2.销毁【从有到无】:

DestroyList(&L):销毁线性表,并释放表L所占有的内存空间;

2.插入和删除:

2.1.插入:

ListInsert(&L,i,e):在表L的第i个位置插入元素e;

2.2.删除:

ListDelete(&L,i,&e):删除表L的第i个位置的元素,并用e返回删除元素的值

3.查找:

3.1.按值查找:

LocateElem(L,e):在表L中查找具有给定值的元素;

3.2.按位查找:

GetElem(L,i):获取表L中第i位置的元素值;

4.其他操作:

4.1.求表长:

Length(L):返回表L的长度;

4.2.输出:

PrintList(L):按前后顺序输出表L的所有元素;

4.3.判空:

Empty(L):为空返回true,否则返回false;

二、线性表的实现

(一)顺序存储(顺序表):

1.定义:

用顺序存储的方式实现线性表;把逻辑上相邻的元素存储在物理上也相邻的存储单元中;
如第一个元素的位置是K,则第二个元素的位置是K+这个元素的大小【sizeof(ElemType)】,例如:

typedef struct{
	int num;
	int people;
}Customer;

一个int占4个字节(byte),则此结构占有8个字节,sizeof(Customer) = 8B;

2.实现方式:

2.1.静态存储:
2.1.1.基本格式:
#define MaxSize 10;//表的最大长度

typedef struct{
	ElemType data(MaxSize);//用静态数组存储,ElemType:代表所有数据类型,使用时用int等类型替换
	int length;//当前长度
}Sqlist;//类型定义,此结构的别名
2.1.2.C语言实现:
//定义顺序表的静态存储结构
#define MaxSize 10	//定义静态存储最大内存为10 
typedef struct{
	int data[MaxSize];//用静态数组存储元素 
	int length; //当前表长 
}SqList;

//初始化表 
void InitList(SqList &L){
	for(int i = 0;i < MaxSize;i ++){
		L.data[i] = 0;//每个数组赋默认值为0,作用:清除之前储存的脏数据
		L.length = 0;//当前表长为0 
	}
	
} 

int main(){
	SqList L;//定义结构,类似于int L 
	InitList(L);
	return 0;
} 

如果数组存满了怎么办:没办法,呵呵🙂!

2.2.动态分配:
2.2.1.基本格式:
#define InitSize 10
typedef struct{
	ElemType* data;//动态分配数组的指针
	int MaxSize;//表的最大容量
	int length;//当前表长
}SeqList;

注:
动态申请和释放内存空间:malloc申请,free释放
malloc申请一整片的内存空间:L.data = (ElemType **)malloc(sizeof(ElemType) * InitSize);

其中:
(ElemType **)为强制转换为自己所需要的数据元素类型
(sizeof(ElemType):所使用的数据类型大小         InitSize:初始长度
sizeof(ElemType) * InitSize:数据类型大小 x 初始长度 = 总大小

2.2.2.C语言实现:
#include <stdio.h> 
#include <stdlib.h>//调用malloc的库函数 ,或#include <malloc.h> 

//顺序表的动态分配
#define InitSize 10//默认最大长度 
typedef struct{
	int* data;//动态分配数组的指针 
	int MaxSize;//最大容量 
	int length;//当前长度 
}SeqList; 

//初始化顺序表
void InitList(SeqList &L){
	L.data = (int*)malloc(sizeof(int)*InitSize);//用malloc申请一整块内存
	L.length = 0;
	L.MaxSize = InitSize;//最大容量为最大长度 
} 

//增加动态数组的长度
void IncreaseSize(SeqList &L,int len){
	int* p = L.data;//将L.data数据暂存在p中
	L.data = (int*)malloc(sizeof(int)*(L.MaxSize+len)); //申请+len大小的地址 
	for(int i = 0;i < L.length;i ++){
		L.data[i] = p[i];//数据转移到新地址 
	}
	L.MaxSize = L.MaxSize+len;
	free(p);//释放p指针 
} 

int main(){
	SeqList L;
	InitList(L);
	IncreaseSize(L,10);
	return 0;
}

3.顺序表的特点:

随机访问:可以在O(1)时间内找到第i个元素,因为存放位置连续存放
存储密度高:每个结点只存储数据元素,不需要存储指针等
拓展不方便:即使采用动态分配,拓展长度的时间复杂度也比较高
插入,删除不方便

4.顺序表的基本操作:

4.1.插入:

ListInsert(&L,i,e);

4.1.1.代码实现:
#include <stdio.h>

//基于静态分配的线性表定义
#define MaxSize 10
typedef struct{
	int data[MaxSize];
	int length;
}SqList; 

//初始化线性表
void InitList(SqList &L){
	for(int i = 0;i < MaxSize;i ++){
		L.data[i] = 0;
		L.length = 0;
	}
} 

//在第i个位置插入元素e
bool ListInsert(SqList &L,int i,int e){//bool用于返回异常情况 
	//条件判断
	if(i<1 || i>L.length+1){//判断i的有效范围 
		return false;
	} 
	if(L.length > MaxSize || L.length == MaxSize){//判断存储空间是否已满 
		return false;
	}
	//将第i个元素及之后的后移
	for(int j = L.length;j >= i;j --){
		L.data[j] = L.data[j - 1];
	} 
	//在位置i插入元素e
	L.data[i-1] = e;//数组开始下标为0 
	L.length ++; 
	return true;
}

int main(){
	SqList L;
	InitList(L);
	ListInsert(L,3,4);
	
}
4.1.2.时间复杂度分析:

关注最深层循环的语句:

	for(int j = L.length;j >= i;j --){
		L.data[j] = L.data[j - 1];
	} 

问题规模:n = L.length(表长)
最好情况:新元素插在表尾,需要移动0个元素,时间复杂度=O(1);
最坏情况:新元素插在表头,需要将原有的n个元素全部移动,当i = 1时,循环n次,时间复杂度=O(n);
平均情况:假设新元素插入到任何位置的概率都相同,即i = 1,2,3,…,length+1的概率为P=1/(n+1)
当i=1时,循环n次;i=2时,循环-1次;i=3时,循环n-2次,…,i=n时,循环0次
则平均循环次数为:nP+(n-1)P+(n-2)P+…+1P=n/2,则时间复杂度=O(n);

4.2.删除:

ListDelete(&L,i,e);

4.2.1.代码实现:
//用静态数组表示顺序表
#define MaxSize 10
typedef struct{
	int data[MaxSize];
	int length;
}SqList; 

//初始化顺序表
void InitList(SqList &L){
	for(int i = 0;i < MaxSize;i ++){
		L.data[i] = 0;//给数据元素赋默认值0
		L.length = 0;//初始长度为0 
		 
	}
} 

//在第i个位置插入元素e
bool ListInsert(SqList &L,int i,int e){
	//是否满足插入的条件判断 ,不满足返回false 
	if(i < 1 || i > L.length+1){
		return false;
	}
	if(L.length > MaxSize || L.length == MaxSize){
		return false;
	}
	
	//i及其后的原有元素依次后移一位 
	for(int j = L.length;j >= i;j --){
		L.data[j] = L.data[j-1];
	}
	//元素e插入,返回true 
	L.data[i-1] = e;
	L.length++;
	return true;
	
} 

//删除表中第i个位置的元素,并且用e返回此元素值 
bool ListDelete(SqList &L,int i,int &e){//&e便于返回e的值 
	//条件判断 
	if(i < 1 || i > L.length){
		return false;
	}
	
	//首先e接收第i个位置的值 
	e = L.data[i-1];
	//然后i之后的元素依次前移 
	for(int j = i;i < L.length;j ++){
		L.data[j-1] = L.data[j];
	}
	L.length--;
	return true;
	
} 

int main(){
	SqList L;
	//初始化顺序表
	InitList(L);
	//插入元素 

	// 删除L表第3个位置的元素,并由e返回其值 
	int e = -1; 
	ListDelete(L,2,e);
	if(ListDelete(L,2,e)){
		printf("已删除第2个元素,删除值为:%d\n",e);
	}else{
		printf("删除失败!");
	}
	return 0;
} 
4.2.2.时间复杂度分析:
for(int j = i;i < L.length;j ++){
	L.data[j-1] = L.data[j];
}

最好情况:删除表尾元素,需要移动0个元素,时间复杂度=O(1);
最坏情况:删除表头元素,需要将原有的n-1个元素全部移动,当i = 1时,循环n-1次,时间复杂度=O(n);
平均情况:假设删除任何一个元素的概率都相同,即i = 1,2,3,…,length+1的概率为P=1/n
当i=1时,循环n-1次;i=2时,循环-2次;i=3时,循环n-3次,…,i=n时,循环0次
则平均循环次数为:(n-1)P+(n-2)P+…+1P=(n-)/2,则时间复杂度=O(n);

5.按位查找:

GetElem(L,i):获取表L中第i个位置的元素值

5.1.代码实现:

I.若顺序表使用静态分配:

#define MaxSize 10
typedef struct{
	ElemType data[MaxSize];
	int length;
}SqList;

则按位查找为:

ElemType GetElem(SqLit L,int i){
return L.data[i-1];
}

II.若线性表使用动态分配:

#define InitSize 10
typedef struct{
	ElemType* data;
	int MaxSize;
	int length;
}SeqList;

则按位查找为:

ElemType GetElem(SeqList L.int i){
	return L.data[i-1];//指针可以认为数组,和数组一样的操作
}
5.2.时间复杂度分析:

因为没有循环等,只是单纯的返回,所以事件复杂度为O(1)

6.按值查找:

LocatElem(L,e):在表L中查找具有给定值的元素

6.1.代码实现:
//动态分配
#define InitSize 10
typedef struct{
	ElemType* data;
	int MaxSize;
	int length;
}SeqList;

//按值查找
int LocateElem(SeqList L,ElemType e){
	for(int i = 0;i < L.length;i++){
		if(L.data[i] == e){
			return i+1;//数组下标为i的元素等于e,其位序为i+1
		}
	}
	return 0;//查询失败
}
6.2.时间复杂度分析:

最好情况:数组第一个元素就匹配,时间复杂度=O(1)
最坏情况:数组最后一个才匹配,时间复杂度=O(n)
平均情况:假设目标元素出现在任何位置的概率都相同,都是1/n
目标元素在第1位,循环1次;在低2位,循环2次;…;在第n位,循环n次
则,平均循环次数:11/n+21/n+…+n*1/n = (n+1)/2,则平均时间复杂度=O(n)

(二)链式存储(链表):

1.单链表:

1.1.定义:

在这里插入图片描述
每个结点除了存放数据元素外,还需要存储指向下一个结点的指针
特点:
不要求大片的连续空间,方便改变容量
不可随机存取,需要耗费一定的空间存放指针

复习,顺序表的特点:
随机访问:可以在O(1)时间内找到第i个元素,因为存放位置连续存放
存储密度高:每个结点只存储数据元素,不需要存储指针等
拓展不方便:即使采用动态分配,拓展长度的时间复杂度也比较高
插入,删除不方便

1.2.代码实现:
typedef struct LNode{//定义结点类型 
	ElemType data;//数据域
	struct LNode* next;//指针域 
}LNode,*LinkList;//用LinkList表示这是struct LNode的指针,可以是LNode* 也可以是LinkList,相等
//当代码强调这是一个单链表时,使用LinkList;强调这是一个指针时,使用LNod*

//动态申请空间,p指针指向此空间
LinkList p = (struct LNode*)malloc(sizeof(struct LNode));
1.2.1.带头结点:
typedef struct LNode{
	ElemType data;
	struct LNode* next;
}LNode,*LinkList;

//初始化一个空的,带头结点的单链表
bool InitList(LinkList &L){
	L = (LNode*)malloc(sizeof(LNode));//为头结点分配空间
	if(L == NULL){//判断头结点内存是否已分配
		return false;
	}
	L->next = NULL;//头结点之后暂时没有结点,避免脏数据
	return true;
}

void test(){
	LinkList L;
	InitList(L);
}

//判断单链表是否为空
bool Empty(LinkList L){
	return (L->next == NULL);
}
1.2.2.不带头结点:
typedef struct LNode{
	ElemType data;
	struct LNode* next;
}LNode,*LinkList;

//初始化一个空的,不带头结点的单链表
bool InitList(LinkList &L){
	L = NULL;//空表,暂时没有任何结点,防止脏数据
	return true;
}

void test(){
	LinkList L;//声明单链表
	InitList(L);
}

//判断单链表是否为空
bool Empty(LinkList l){
	return (L == NULL);
}

带不带头结点的特点:
不带头节点,写代码麻烦,对第一个数据结点和后续结点的处理需要用不同的代码逻辑,对空表和非空表的处理需要不同的代码逻辑
带头结点,写代码方便

1.3.基本操作:
1.3.1.插入:
1.3.1.1.按位序插入:

I.带头结点:
ListInsert(&L,i,e):需要找到第i-1个结点,将新结点插入
比如:在第2个位置插入元素e,即是找到第1个结点,在此结点的next域申请空间,并插入

#include <stdio.h>
#include <stdlib.h>//nalloc()函数的调用库 

//定义单链表
typedef struct LNode{
	int data;
	struct LNode* next;
}LNode,*LinkList; 

//初始化单链表(带头结点)
bool InitList(LinkList &L){
	L = (LNode*)malloc(sizeof(LNode));
	if(L == NULL){//头结点判空条件 
		return false;
	}
	L->next = NULL; 
	return true; 
} 

//在第i个位置插入元素e
bool ListInsert(LinkList &L,int i,int e){
	//是否满足插入的条件判断
	if(i < 1){
		return false;
	} 
	
	//具体插入实现 
	LNode* p;
	p = L;//将L的头指针给p 
	if(p == NULL){//若头指针为NULL则返回false 
		return false; 
	} 
	int j = 0; 
	while(p != NULL && j < i-1){
		p = p->next;//所有元素后移 
		j++;
	} 
	LNode* s = (LNode*)malloc(sizeof(LNode)); //为插入位置开辟空间 
	s->data = e;//元素e放入s的数据域
	s->next = p->next;//s的指针域指向p+1的数据域 
	p->next = s;//p的指针域指向s的数据域 
	return true; 
} 

int main(){
	LinkList L;
	InitList(L);
	ListInsert(L,2,3);
}

II.不带头结点:

#include <stdio.h>
#include <stdlib.h>

//定义一个单链表
typedef struct LNode{
	int data;
	struct LNode* next;
}LNode,*LinkList; 

//初始化单链表(不带头结点) 
bool InitList(LinkList &L){
	L = NULL;
	return true;
}

//插入元素
bool ListInsert(LinkList &L,int i,int e){
	//第1个位置插入元素e 
	if(i == 1){
		LNode* s = (LNode*)malloc(sizeof(LNode));
		s->data = e;
		s->next = L;//s的指针域指向原有的L链表 
		L = s;//将s赋给L 
		return true;
	} 
	//其他位置插入元素e
	LNode* p;
	int j = 1;//注意此时j为1
	p = L;
	while(p != NULL && j < i-1){
		p = p->next;
		j++;
	}
	if(p == NULL){
		return false;
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
} 
1.3.2.指定结点的后插:

后插:给定一个结点,在此结点之后插入元素e

#include <stdio.h>
#include <stdlib.h> 

//定义单链表
typedef struct LNode{
	int data;
	struct LNode* next;
}LNode,*LinkList; 

//初始化单链表(带头结点) 
bool InitList(LinkList &L){
	L = (LNode*)malloc(sizeof(LNode)); 
	L->next = NULL;
	return true;
	if(L == NULL){
		return false;
	}
}

//在第i个位序插入元素e
bool ListInsert(LinkList &L,int i,int e){
	//条件判断
	if(i < 1){
		return false;
	} 
	//将L的头结点赋给p 
	LNode* p;
	p = L;
	//从头结点开始,依次移动指针指到位序i 
	if(p == NULL){
		return false;
	}
	int j = 0; 
	while(p != NULL && j < i-1){
		p = p->next;
		j++;	
	} 
	//给s指针开票空间,存放e,并且插入到p中 
	LNode* s = (LNode*)malloc(sizeof(LNode)); 
	s->data = e;
	s->next =  p->next;
	p->next = s;
	return true;
} 

//后插操作,在元素p的后面插入元素e
bool InsertNextLNode(LNode* p,int e){
	if(p == NULL){
		return false;
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));
	if(s == NULL){//空间分配失败 
		return false; 
	}
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
} 
1.3.3.指定结点的前插:

在指定结点前面插入元素e

#include <stdio.h>
#include <stdlib.h> 

//定义单链表
typedef struct LNode{
	int data;
	struct LNode* next;
}LNode,*LinkList; 

//初始化单链表(带头结点) 
bool InitList(LinkList &L){
	L = (LNode*)malloc(sizeof(LNode)); 
	L->next = NULL;
	return true;
	if(L == NULL){
		return false;
	}
}

//在第i个位序插入元素e
bool ListInsert(LinkList &L,int i,int e){
	//条件判断
	if(i < 1){
		return false;
	} 
	//将L的头结点赋给p 
	LNode* p;
	p = L;
	//从头结点开始,依次移动指针指到位序i 
	if(p == NULL){
		return false;
	}
	int j = 0; 
	while(p != NULL && j < i-1){
		p = p->next;
		j++;	
	} 
	//给s指针开票空间,存放e,并且插入到p中 
	LNode* s = (LNode*)malloc(sizeof(LNode)); 
	s->data = e;
	s->next =  p->next;
	p->next = s;
	return true;
} 

//后插操作,在元素p的后面插入元素e
bool InsertNextLNode(LNode* p,int e){
	if(p == NULL){
		return false;
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));
	if(s == NULL){//空间分配失败 
		return false; 
	}
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
} 

//前插操作,在元素p的前面插入元素e
bool InsertPriorLNode(LNode* p,int e){
	if(p == NULL){
		return false;
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));
	if(s == NULL){
		return NULL;
	}
	s->next = p->next;//S的next域指向p的下一结点 
	p->next = s;//p的next域指向s的数据域 
	s->data = p->data;//将p的数据赋到s的数据域里 
	p->data = e;//将元素e赋值到p的数据域里 
	return true;
} 

在这里插入图片描述

1.3.2.删除:
1.3.2.1.按位序删除:

I带头结点:
ListDelete(&L,i,&e):删除表L中第i个位置的元素,并用e返回元素的值,需要找到第i-1个结点,将其指向第i+1个结点,并释放第i个结点。

#include <stdio.h>
#include <stdlib.h> 

//定义单链表
typedef struct LNode{
	int data;
	struct LNode* next;
}LNode,*LinkList; 

//初始化单链表(带头结点) 
bool InitList(LinkList &L){
	L = (LNode*)malloc(sizeof(LNode)); 
	L->next = NULL;
	return true;
	if(L == NULL){
		return false;
	}
}

//在第i个位序插入元素e
bool ListInsert(LinkList &L,int i,int e){
	//条件判断
	if(i < 1){
		return false;
	} 
	//将L的头结点赋给p 
	LNode* p;
	p = L;
	//从头结点开始,依次移动指针指到位序i 
	if(p == NULL){
		return false;
	}
	int j = 0; 
	while(p != NULL && j < i-1){
		p = p->next;
		j++;	
	} 
	//给s指针开票空间,存放e,并且插入到p中 
	LNode* s = (LNode*)malloc(sizeof(LNode)); 
	s->data = e;
	s->next =  p->next;
	p->next = s;
	return true;
} 

//后插操作,在元素p的后面插入元素e
bool InsertNextLNode(LNode* p,int e){
	if(p == NULL){
		return false;
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));
	if(s == NULL){//空间分配失败 
		return false; 
	}
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
} 

//前插操作,在元素p的前面插入元素e
bool InsertPriorLNode(LNode* p,int e){
	if(p == NULL){
		return false;
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));
	if(s == NULL){
		return NULL;
	}
	s->next = p->next;//S的next域指向p的下一结点 
	p->next = s;//p的next域指向s的数据域 
	s->data = p->data;//将p的数据赋到s的数据域里 
	p->data = e;//将元素e赋值到p的数据域里 
	return true;
} 

//按位序删除
bool ListDelete(LinkList &L,int i,int &e){//e需要带回,则需要&
	if(i < 1){
		return false;
	}
	LNode* p;
	p = L;
	int j = 0;
	while(p != NULL && j < i -1){
		p = p->next;//循环完成后,p指针指向i-1个结点的位置,即若删除第4个元素 ,则目前p指向第3个 
		j++;
	}
	if(p == NULL){
		return false;
	}
	if(p->next == NULL){
		return false; 
	}
	LNode* q = p->next;//q指针指向p
	e = q->data;//q的数据赋给e 
	p->next = q->next;//p直接指向q的下一个结点,即孤立q结点 
	free(q);
	return true; 
	
} 

在这里插入图片描述

1.3.2.2.指定结点的删除:

bool DeleteNode(LNode *p):删除结点p,需要知道p的前一个结点

bool DeleteNode(LNode* p){
	if(p == NULL){
		return false;
	}
	LNode* q = p->next;//循环后,p指向i-1个位置,则q结点指向p
	p->data = q->data;//数据交换
	p->next = q->next;//i的前一个结点(i-1),直接指向q的下一个结点(i+1),即绕过了q
	free(q);
	return true;
} 
1.3.3.查找:
1.3.3.1.按位查找:

GetElem(L,i):获取表L中第i个元素的值

bool GetElem(LinkList L,int i){
	if(i < 0){
		return false;
	}
	LNode* p;
	p = L;
	int j = 0;
	while(p != NULL && j < i){
		p = p->next;
		j ++;
	}
	return p;
} 

1.3.3.2.按值查找:

LocateElem(L,E):在表L中查找具有给定关键字值的元素

bool LocateElem(LinkList L,int e){
	LNode* p =L->next;
	while(p != NULL && p->data != e){//从第一个结点开始查找 
		p = p->next;
	}
	return p;
}
1.3.4.求表长:
int Length(LinkList L){
	int len = 0;//统计表长 
	LNode* p = L;
	while(p->next != NULL){
		p = p->next;
		len++;
	}
	return len;
} 
1.3.5.单链表的建立:

如果给你很多数据元素,要求把他们存到一个单链表中,咋neng呢?
在这里插入图片描述首先:初始化一个单链表
其次:每次去一个还俗,插入到表尾(尾插)/表头(头插)

1.3.5.1.尾插法:
bool TailInsert(LinkList L){
	L = (LinkList)malloc(sizeof(LNode));//创建头结点 
	L->next = NULL;
	int x;
	scanf("%d\n",&x);
	LNode* s,*r = L;//s为插入的元素,r为表尾元素 
	while(x != 9999){//当输入9999表示插入结束 
		s = (LNode*)malloc(sizeof(LNode)); 
		s->data = x;//插入元素放入s的数据域中 
		r->next = s;//表尾的next域指向插入的元素s 
		r = s;//保证r一直指向表L的表尾 
		scanf("%d\n",&x); 
	}
	r->next = NULL;
	return L;
} 

1.3.5.2.头插法(逆置,输入4 5 6,插入为6 5 4):
bool HeadInsert(LinkList L){
	L = (LinkList)malloc(sizeof(LNode));//创建头结点 
	L->next = NULL;//避免脏数据 
	int x;
	scanf("%d\n",&x);
	LNode* s;
	while(x != 9999){//当输入9999表示插入结束 
		s = (LNode*)malloc(sizeof(LNode)); 
		s->data = x;
		s->next = L->next;
		L->next = s;
		scanf("%d\n",&x); 
	}	
	return L;
}

在这里插入图片描述

1.4.代码合集:
#include <stdio.h>
#include <stdlib.h> 

//定义单链表
typedef struct LNode{
	int data;
	struct LNode* next;
}LNode,*LinkList; 

//初始化单链表(带头结点) 
bool InitList(LinkList &L){
	L = (LNode*)malloc(sizeof(LNode)); 
	L->next = NULL;
	return true;
	if(L == NULL){
		return false;
	}
}

//在第i个位序插入元素e
bool ListInsert(LinkList &L,int i,int e){
	//条件判断
	if(i < 1){
		return false;
	} 
	//将L的头结点赋给p 
	LNode* p;
	p = L;
	//从头结点开始,依次移动指针指到位序i 
	if(p == NULL){
		return false;
	}
	int j = 0; 
	while(p != NULL && j < i-1){
		p = p->next;
		j++;	
	} 
	//给s指针开票空间,存放e,并且插入到p中 
	LNode* s = (LNode*)malloc(sizeof(LNode)); 
	s->data = e;
	s->next =  p->next;
	p->next = s;
	return true;
} 

//后插操作,在元素p的后面插入元素e
bool InsertNextLNode(LNode* p,int e){
	if(p == NULL){
		return false;
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));
	if(s == NULL){//空间分配失败 
		return false; 
	}
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
} 

//前插操作,在元素p的前面插入元素e
bool InsertPriorLNode(LNode* p,int e){
	if(p == NULL){
		return false;
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));
	if(s == NULL){
		return NULL;
	}
	s->next = p->next;//S的next域指向p的下一结点 
	p->next = s;//p的next域指向s的数据域 
	s->data = p->data;//将p的数据赋到s的数据域里 
	p->data = e;//将元素e赋值到p的数据域里 
	return true;
} 

//按位查找,返回第i个元素
bool GetElem(LinkList L,int i){
	if(i < 0){
		return false;
	}
	LNode* p;
	p = L;
	int j = 0;
	while(p != NULL && j < i){
		p = p->next;
		j ++;
	}
	return p;
} 

//按值查找,找到数据域==e的结点
bool LocateElem(LinkList L,int e){
	LNode* p =L->next;
	while(p != NULL && p->data != e){//从第一个结点开始查找 
		p = p->next;
	}
	return p;
}

//求表长:
int Length(LinkList L){
	int len = 0;//统计表长 
	LNode* p = L;
	while(p->next != NULL){
		p = p->next;
		len++;
	}
	return len;
} 

//尾插法建立单链表
bool TailInsert(LinkList L){
	L = (LinkList)malloc(sizeof(LNode));//创建头结点 
	L->next = NULL;
	int x;
	scanf("%d\n",&x);
	LNode* s,*r = L;//s为插入的元素,r为表尾元素 
	while(x != 9999){//当输入9999表示插入结束 
		s = (LNode*)malloc(sizeof(LNode)); 
		s->data = x;//插入元素放入s的数据域中 
		r->next = s;//表尾的next域指向插入的元素s 
		r = s;//保证r一直指向表L的表尾 
		scanf("%d\n",&x); 
	}
	r->next = NULL;
	return L;
} 

 //尾插法建立单链表
bool HeadInsert(LinkList L){
	L = (LinkList)malloc(sizeof(LNode));//创建头结点 
	L->next = NULL;//避免脏数据 
	int x;
	scanf("%d\n",&x);
	LNode* s;
	while(x != 9999){//当输入9999表示插入结束 
		s = (LNode*)malloc(sizeof(LNode)); 
		s->data = x;
		s->next = L->next;
		L->next = s;
		scanf("%d\n",&x); 
	}	
	return L;
}

int main(){
	return 0;
}

2.双链表:

typedef struct DNode{
	ElemType data;
	struct DNode *prior,*next;
}DNode,*DLinkList;
2.1.初始化:
bool InitDLinkList(DLinkList &L){
	L = (DNode*)malloc(sizeof(DNode));
	if(L == NULL){
		return false;
	}
	L->prior = NULL;
	L->next = NULL;
	return true;
} 
2.2.插入:
bool InsertNextDNode(DNode* p,DNode* s){
	if(p == NULL || s == NULL){
		return false;
	}
	s->next = p->next;
	if(p->next != NULL){//避免空指针异常 
		p->next->prior = s;
	}
	s->prior = p;
	p->next = s;
	return true; 
} 
2.3.删除:
bool DNodeDelete(DNode* p){
	if(p ==NULL){
		return false; 
	}
	DNode* q = p->next;
	if(q == NULL){
		return false;
	}
	p->next = q->next;
	if(q->next != NULL){
		q->next->prior = p;
	}
	free(q);
	return true;
} 

//销毁
void DestoryList(DLinkList &L){
	while(L->next != NULL){
		DNodeDelete(L);
	}
	free(L);
	L = NULL;
} 
2.4.遍历:
//自前向后
while(p != NULL){
	p = p->next;
}
//自后向前
while(p != NULL){
	p = p->prior;
}
2.5.代码合集:
#include <stdio.h>
#include <stdlib.h>

//创建双链表
typedef struct DNode{
	int data;
	struct DNode* prior, *next;//声明头结点和尾结点 
}DNode,*DLinkList; 

//初始化双链表
bool InitDLinkList(DLinkList &L){
	L = (DNode*)malloc(sizeof(DNode));
	if(L == NULL){
		return false;
	}
	L->prior = NULL;
	L->next = NULL;
	return true;
} 

//双链表的插入,p结点之后插入s 
bool InsertNextDNode(DNode* p,DNode* s){
	if(p == NULL || s == NULL){
		return false;
	}
	s->next = p->next;
	if(p->next != NULL){//避免空指针异常 
		p->next->prior = s;
	}
	s->prior = p;
	p->next = s;
	return true; 
} 

//删除
bool DNodeDelete(DNode* p){
	if(p ==NULL){
		return false; 
	}
	DNode* q = p->next;
	if(q == NULL){
		return false;
	}
	p->next = q->next;
	if(q->next != NULL){
		q->next->prior = p;
	}
	free(q);
	return true;
} 

//销毁
void DestoryList(DLinkList &L){
	while(L->next != NULL){
		DNodeDelete(L);
	}
	free(L);
	L = NULL;
} 

3.循环链表:

3.1.循环单链表:

单链表的最后结点指针指向NULL,循环单链表最后结点指针指向头结点
在这里插入图片描述

#include <stdio.h> 
#include <stdlib.h>

//定义循环单链表 
typedef struct LNode{
	int data;
	struct LNode* next;
}LNode,*LinkList;

//初始化循环单链表
bool InitList(LinkList &L){
	L = (LNode*)malloc(sizeof(LNode));
	if(L == NULL){
		return false;
	}
	L->next = L;//头指针的next域指向头结点 
	return true;
} 

//判表空
bool Empty(LinkList L){
	if(L->next == L){
		return true;
	}else{
		return false;
	}
} 

//判断结点p是否为循环单链表L的表尾结点
bool isTail(LinkList L,LNode* p){
	if(p->next == L){
		return true;
	}else{
		return false;
	}
} 
3.2.循环双链表:

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>

//定义一个循环双链表
typedef struct DNode{
	int data;
	struct DNode* prior;
	struct DNode* next;
}DNode,*DLinkList; 

//初始化循环双链表
bool InitList(DLinkList &L){
	L = (DNode*)malloc(sizeof(DNode));
	if(L == NULL){
		return false;
	}
	L->next = L;
	L->prior = L;
	return true;
} 

//判表空
bool Empty(DLinkList L){
	if(L->next == L){
		return true;
	}else{
		return false;
	}
} 

//判断结点p是否为表尾结点
bool isTail(DLinkList L,DNode* p){
	if(p->next == L){
		return true;
	}else{
		return false;
	}
} 

4.静态链表:

4.1.概念:

不同于单链表,单链表各个结点的内存是零散的分布在内存空间中,通过指针连接;而静态链表需要分配一整片连续的内存空间,各个结点集中安置,每个结点包含数据元素和下一个结点的数组下标,下标为0的结点充当头结点,每个数据元素4B,每个游标4B

4.2.定义:
#include <stdio.h>
#include <stdlib.h>

//定义静态链表
#define MaxSize 10
typedef struct Node{
	ElemType data;//储存的数据元素 
	int next;//下一个元素的数组下标 
}SLinkList[MaxSize]; //重命名为数组,后面写为:SLinkList a,a即为数组 

5.顺序表和链表对比:

5.1.逻辑结构:

都是线性表,线性结构

5.2.物理结构/存储结构:

顺序表支持随机存取,存储密度高,但需要大片连续的空间,不方便改变容量
链表只需要离散的小空间,便于分配,改变容量方便,但不可随机存取,存储密度低

5.3.数据运算/基本操作:

创建:顺序表虽容量可以用malloc改变,但需要移动大量元素,时间代价大
销毁:malloc和free成对出现,链表都是用malloc创建的,而顺序表的静态数组自动回收
增删:顺序表需要移动大量后续元素,链表只需要修改指针
改查:顺序表可以可以随机修改
在这里插入图片描述

5.4.如何抉择:

表长难以估计,需要经常增删——链表
表长可估计,查询搜索多——顺序表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值