2.1 线性表的定义和特点
线性表是抽象数据类型中的一种。
线性表的定义:由n(n≥0)个数据特性相同的元素构成的有限序列称为线性表。
2.2 线性表的案例引入
典型的两种线性表的类型:
(1)多项式的线性表
又分为两种:
①非稀疏一元多项式的线性表
②稀疏一元多项式的线性表
(2)图书信息管理系统式的线性表
2.3 线性表的类型定义
//(1)线性表的抽象数据类型定义部分
ADT List{
数据对象:D = { ai|ai∈ElemSet,i = 1,2,…,n,n≥0 }
数据关系:R = { <a(i - l),ai> | a(i - l),ai∈D,i = 2,…,n }
基本操作:
InitList(&L)
操作结果:构造一个空的线性表L。
DestroyList(&L)
初始条件:线性表L已存在。
操作结果:销毁线性表L。
ClearList(&L)
初始条件:线性表L已存在。
操作结果:将L重置为空表。
ListEmpty(L)
初始条件:线性表L已存在。
操作结果:若L为空表,则返回true, 否则返回false.
ListLength(L)
初始条件:线性表L已存在。
操作结果:返回L中数据元素个数。
GetElem(L, i, &e)
初始条件:线性表L已存在,且l≤i≤ListLength(L)。
操作结果:用e返回L中第i个数据元素的值。
LocateElem(L, e)
初始条件:线性表L已存在。
操作结果:返回L中第1个值与e相同的元素在L中的位置。若这样的数据元素不存在,则返回值为0。
PriorElem(L, cur_e, &pre_e)
初始条件:线性表L已存在。
操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回其前驱,否则操作失败,pre_e无定义。
NextElem(L, cur_e, &next_e)
初始条件:线性表L已存在。
操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回其后继,否则操作失败,next_e无定义。
ListInsert(&L, i, e)
初始条件:线性表L已存在,且1≤i≤ListLength(L) + 1。
操作结果:在L中第i个位置之前插入新的数据元素e, L的长度加1。
ListDelete(&L, i)
初始条件:线性表L已存在且非空,且1≤i≤ListLength(L)。
操作结果:删除L的第i个数据元素,L的长度减1。
TraverseList(L)
初始条件:线性表L已存在。
操作结果:对线性表L进行遍历,在遍历过程中对L的每个结点访问一次。
}ADT List
2.4 线性表的顺序表示和实现
2.4.1 线性表的顺序存储表示
顺序表的定义:
又称线性表的顺序表示、线性表的顺序存储结构、线性表的顺序映像。
指的是用一组地址连续的存储单元依次存储线性表的数据元素。
在C语言中,采用动态分配的一维数组这一数据类型表示线性表。
关于"用C语言中的一维数组描述线性表(这一抽象数据类型)数据对象的顺序存储结构"的解释:
其实并没有直接使用char s[LENGTH]这样的形式,而是利用了该“一组地址连续的存储单元”的①基地址(具体地定义了指向ElemType类型的指针elem,且规定指针elem指向了该“一组地址连续的存储单元”的首个地址)及其②长度length,并将elem和length作为结构成员,构造了一个结构SqList,并为其分配存储空间。
再将结构(数据)类型的SqList作为该顺序表真正的存储结构表示方式。
【针对每种抽象数据类型定义中的数据对象,其数据存储结构要借助某一数据类型来描述。
在顺序表的存储结构中,即套用固定的格式,且利用明确了的这句话中的三个变量书写出来:
第一个变量:某种抽象数据类型,即线性表;
第二个变量:数据存储结构,即顺序存储结构;
第三个变量:某一数据类型,即C语言中的一维数组(见上面的解释部分)。】
//(2)抽象数据类型数据对象的存储结构表示部分
//(2)①顺序表的存储结构(即地址连续的线性表的顺序存储结构或顺序映像)表示部分
#define MAXSIZE 100
typedef struct //不管是顺序存储结构,还是链式存储结构,均用类型定义(typedef)描述
{
ElemType* elem; /*数据元素类型约定为ElemType,由用户在使用该数据类型时自行定义。
这里使用的数据类型即为C语言中的一维数组(见上面的解释部分)。*/
int length;
}SqList; //该结构数据类型定义后面加了变量名SqList,则会为其分配存储空间。
//这也是线性表中的非稀疏一元多项式类型的线性表的顺序存储结构表示方式
//(2)②稀疏一元多项式的线性表的顺序存储结构的类型定义/表示方式
//仍然属于顺序表的存储结构
#define MAXSIZE 100
/*相比于上面顺序表的存储结构表示方式,此处先自行定义了要使用的结构数据元素类型Polynomial,即约定的ElemType.
且仍然使用类型定义typedef描述。*/
typedef struct
{
float coef;
int expn;
}Polynomial;//该结构数据类型定义后面加了变量名,则会为其分配存储空间。
typedef struct
{
Polynomial* elem; //这里使用的数据类型仍为C语言中的一维数组(见上面的解释部分)。
int length;
}Sqlist;//该结构数据类型定义后面加了变量名,则会为其分配存储空间。
针对每种抽象数据类型定义中的数据对象,其数据存储结构要借助某一数据类型来描述。
这句话中,只要是数据的顺序存储结构,不管是哪种抽象数据类型的数据对象,都采用C语言中的一维数组来描述?
并不是。
这句话中 第三个变化的量,某一数据类型,可能是基本数据类型去描述复杂的抽象数据类型,也可能是用某一复杂的抽象数据类型去描述更复杂的抽象数据类型。
如用二叉链表、十字链表、邻接表、邻接多重表这些复杂的抽象数据类型,去描述树和图等非线性结构,更复杂的抽象数据类型。
//(2)③图书信息管理系统式的线性表的顺序存储结构的类型定义
//仍然属于顺序表的存储结构
#define MAXSIZE 10000
typedef struct
{
char no[20];
char name[50];
float price;
}Book;//该结构数据类型定义后面加了变量名,则会为其分配存储空间。
typedef struct
{
Book* elem;//这里使用的数据类型仍为C语言中的一维数组(见上面的解释部分)。
int length;
}Sqlist;//该结构数据类型定义后面加了变量名,则会为其分配存储空间。
//(3)相应操作的具体实现部分(顺序表)
2.4.2 顺序表中基本操作的实现
2.4.2.1 初始化
//算法2.1 顺序表的初始化
SqList L;
Status InitList(SqList& L)
{
//构造一个空的顺序表L
L.elem = new ElemType[MAXSIZE];
if (!L.elem) exit(OVERFLOW);
L.length = 0;
return OK;
}
2.4.2.2 取值
//算法2.2 顺序表的取值
Status GetElem(SqList L, int i, ElemType &e)
{
if (i < 1 | i > L.length) return ERROR;
e = L.elem[i - 1];
return OK;
}
2.4.2.3 查找
//算法2.3 顺序表的查找
int LocateElem(SqList L, ElemType e)
{
//在顺序表L中查找值为e的数据元素,返回其序号
for (i = 0; i < L.length; i++)
{
if (L.elem[i] == e) return i + 1;
}
return 0;
}
2.4.2.4 插入
//算法2.4 顺序表的插入
Status ListInsert(SqList &L, int i, ElemType e)
{
//在顺序表L中第i个位置之前插入新的元素e,i值的合法范围是1≤i≤L.length+1
if ((i < 1) || (i > L.Length + 1)) return ERROR;
if (L.length == MAXSIZE) return ERROR;
for (j = L.length - 1; j >= i - 1; j--)
{
L.elem[j + 1] = L.elem[j];
}
L.elem[i - 1] = e;
++L.length;
return OK;
}
2.4.2.5 删除
//算法2.5 顺序表的删除
Status ListDelete(SqList &L, int i)
{
//在顺序表L中删除第i个元素,i值的合法范围是1≤i≤L.length
if ((i < 1) || (i > L.length)) return ERROR;
for (j = i; j <= L.length - 1; j++)
{
L.elem[j - 1] = L.elem[j];
}
--L.length;
return OK;
}
2.4.2.6 顺序表基本操作程序实现
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define MAXSIZE 100 //节点个数
typedef int Status; //函数返回类型
typedef int ElemType; //元素类型
//节点结构
typedef struct {
ElemType* elem; //存储数据的数组
int length; //线性表长度
}SqList;
SqList L;
/*函数声明*/
Status InitList(SqList &L); //初始化
Status GetElem(SqList L, int i, ElemType &e);
int LocateElem(SqList L, ElemType e);
Status ListInsert(SqList& L, int i, ElemType e);
Status ListDelete(SqList& L, int i);
int main()
{
int j = 0;
int k = 0;
int m = 0;
int n = 0;
int p = 0;
int q = 0;
ElemType e1=0;
SqList L;
k = InitList(L); //返回的k是初始化状态码
printf("初始化状态码:%d\n", k);
if (k)
{
for (j = 0; j < MAXSIZE-10; j++)
{
L.elem[j] = j;
L.length++;
}
}
m = GetElem(L, 55, e1);//返回的是取值操作状态码
printf("取值操作状态码:%d\n", m);
printf("取值操作结果值:%d\n",e1); //返回的是调用GetElem函数时,找到的顺序表L中下标为55的元素
n = LocateElem(L, 30);
printf("查找操作结果序号值:%d\n", n);
p = ListInsert(L,10,100000);
printf("插入操作状态码:%d\n", p);
printf("插入操作结果值L.elem[9]:%d\n", L.elem[9]);
printf("删除操作前L.elem[49]:%d\n", L.elem[49]);
q = ListDelete(L,50);
printf("删除操作状态码:%d\n", q);
printf("删除操作后结果值L.elem[49]:%d\n", L.elem[49]);
return 0;
}
//算法2.1 顺序表的初始化
Status InitList(SqList &L)
{
//构造一个空的顺序表L
L.elem = new ElemType[MAXSIZE];
if (!L.elem) exit(OVERFLOW);
L.length = 0;
return OK;
}
//算法2.2 顺序表的取值
Status GetElem(SqList L, int i, ElemType& e)
{
if (i < 1 || i > L.length) return ERROR;
e = L.elem[i - 1];
return OK;
}
//算法2.3 顺序表的查找
int LocateElem(SqList L, ElemType e)
{
//在顺序表L中查找值为e的数据元素,返回其序号
for (int i = 0; i < L.length; i++)
{
if (L.elem[i] == e) return i + 1;
}
return 0;
}
//算法2.4 顺序表的插入
Status ListInsert(SqList &L, int i, ElemType e)
{
//在顺序表L中第i个位置之前插入新的元素e,i值的合法范围是1≤i≤L.length+1
if ((i < 1) || (i > L.length + 1)) return ERROR;
if (L.length == MAXSIZE) return ERROR;
for (int j = L.length - 1; j >= i - 1; j--)
{
L.elem[j + 1] = L.elem[j];
}
L.elem[i - 1] = e;
++L.length;
return OK;
}
//算法2.5 顺序表的删除
Status ListDelete(SqList& L, int i)
{
//在顺序表L中删除第i个元素,i值的合法范围是1≤i≤L.length
if ((i < 1) || (i > L.length)) return ERROR;
for (int j = i; j <= L.length - 1; j++)
{
L.elem[j - 1] = L.elem[j];
}
--L.length;
return OK;
}
从此处起,开始讨论抽象数据类型数据对象的链式存储结构表示部分。
2.5 线性表的链式表示和实现
2.5.1 单链表的定义和表示
相比于上面2.4.1中
顺序表的定义: 又称线性表的顺序表示、线性表的顺序存储结构、线性表的顺序映像。
指的是用一组地址连续的存储单元依次存储线性表的数据元素。 在C语言中,采用动态分配的一维数组这一数据类型表示线性表。
链的定义:指针域中存储的信息称作指针或链。
链表的定义:
n个结点(a,(1≤i≤n)的存储映像)链结成个链表,即为线性表(a1,a2,…,an)的链式存储结构,又称线性表的链式(存储)映像。
衍生出线性链表的定义:又称单链表,是链表的每个结点中只包含一个指针域的情况(信息存储在一个指针域中,即指针或链存储在一个指针域中)。
//(2)抽象数据类型数据对象的存储结构表示部分
//(2)④单链表的存储结构(即存储地址任意的线性表的非顺序存储结构、非顺序映像、链式存储结构、链式映像)表示部分
typedef struct LNode//不管是顺序存储结构,还是链式存储结构,均用类型定义(typedef)描述
{
ElemType data; /*数据元素类型约定为ElemType,由用户在使用该数据类型时自行定义。*/
struct LNode *next; /* 这里使用的数据类型为C语言中的"结构指针" */
}LNode,*LinkList; //该结构数据类型定义后面加了结构体的变量名,则会为其分配存储空间。
//(3)相应操作的具体实现部分(单链表)
2.5.2 单链表基本操作的实现
2.5.2.1 初始化
//算法2.6 单链表的初始化
LinkList L; //将L定义为单链表的头指针
Status InitList(LinkList& L)
{
//构造一个空的单链表L
L = new LNode;
L->next = NULL;
return OK;
}
2.5.2.1 C语言程序实现
(使用纯C语言,不适用伪代码,也不使用任何c++语言)
//根据视频教程
#include<stdio.h>
#include<stdlib.h>
struct Node
{
int data;
struct Node* next;
};
struct Node* createList();
int main()
{
struct Node* list = createList();
printf("list->data:%d\n", list->data);
printf("list->next:%d\n", list->next);
return 0;
}
struct Node* createList()//创建链表并进行初始化(用头指针表示一个链表)
{
struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
//结构体指针headNode通过申请动态内存,产生例程,*headNode变成结构体变量
headNode->next = NULL;
return headNode;
}
//链表是结构体变量与结构体变量通过指针连接在一起
/* 动态创建一个链表,即是动态内存申请 + 动态模块化设计
1.创建链表(创建一个表头表示整个链表)
2.创建结点
3.插入结点
4.删除结点
5.打印遍历链表(通常用于测试) */
//课本教材实现版本(可以不看)
#include<stdio.h>
#include<stdlib.h>
#define OK 1
typedef struct LNode
{
int data; //ElemType为int类型
struct LNode* next;
}LNode,*LinkList;
LinkList L;//定义单链表的头指针,等价于上面版本的struct Node* headNode
typedef int Status;
Status InitList(LinkList &L);
int main()
{
int n = 0;
n = InitList(L);
printf("InitList Status:%d\n",n);
return 0;
}
Status InitList(LinkList &L)
{
//构造一个空的单链表L
L = new LNode; //等价于上面的struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
L->next = NULL;
return OK;
}
2.5.2.2 创建结点C语言实现
#include<stdio.h>
#include<stdlib.h>
struct Node
{
int data;
struct Node* next;
};
struct Node* newNode; //不能省略
struct Node* createList();
struct Node* createNode(int data);
int main()
{
struct Node* list = createList();
newNode = createNode(10);
printf("newNode->data:%d\n",newNode->data);
printf("newNode->next:%d\n", newNode->next);
return 0;
}
struct Node* createList()//创建链表并进行初始化(用头指针表示一个链表)
{
struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
//结构体指针headNode通过申请动态内存,产生例程,*headNode变成结构体变量
headNode->next = NULL;
return headNode;
}
struct Node* createNode(int data) //创建结点
{
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
};
2.5.2.3 取值
//算法2.7 单链表的取值
Status GetElem(LinkList L, int i, ElemType &e)
{
//在带头结点的单链表L中根据序号i获取元素的值,用e返回L中第i个数据元素的值
p = L->next; j = 1;
while(p&&j<i)
{
p = p->next;
++j;
}
if (( !p && (j != i)) || j > i) return ERROR;
e = p->data;
return OK;
}
2.5.2.3 C语言程序实现
//根据视频教程
#include<stdio.h>
#include<stdlib.h>
struct Node
{
int data;
struct Node* next;
};
struct Node* newNode; //不能省略
struct Node* createList();
struct Node* createNode(int data);
void insertNodeByHead(struct Node* headNode, int data);
void printList(struct Node* headNode);
int GetElem(struct Node* headNode, int i);
int main()
{
struct Node* list = createList();
insertNodeByHead(list, 1); //从第二个位置开始存的数据
insertNodeByHead(list, 2);
insertNodeByHead(list, 3);
printList(list);
int e1,e2,e3,e4,e5 = 0;
e1 = GetElem(list, 1); //头结点中没有被存入数据
e2 = GetElem(list, 2);
e3 = GetElem(list, 3);
e4 = GetElem(list, 4);
printf("e1 : %d\n", e1); //头结点中没有被存入数据
printf("e2 : %d\n", e2);
printf("e3 : %d\n", e3);
printf("e4 : %d\n", e4);
return 0;
}
struct Node* createList()//创建链表并进行初始化(用头指针表示一个链表)
{
struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
//结构体指针headNode通过申请动态内存,产生例程,*headNode变成结构体变量
headNode->next = NULL;
return headNode;
}
struct Node* createNode(int data) //创建结点
{
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
};
//插入结点(头插法)
//参数:插入哪个链表,插入结点的数据是多少
void insertNodeByHead(struct Node* headNode, int data)
{
//1.创建要插入的结点
struct Node* newNode = createNode(data);
newNode->next = headNode->next; //不往头结点中存数据
headNode->next = newNode;
};
//遍历打印结点(不往头结点存储数据,也不打印头结点,均从第二个位置开始)
void printList(struct Node* headNode)
{
struct Node* pMove = headNode->next;
while (pMove)
{
printf("%d", pMove->data);
pMove = pMove->next;
}
printf("\n");
}
int GetElem(struct Node* headNode, int i)
{
//在带头结点的单链表L中根据序号i获取元素的值,并返回
struct Node* p = headNode;
int j = 1;
int e = 0;
while (p && j < i)
{
p = p->next;
++j;
}
if ((!p && (j != i)) || j > i)
{
printf("ERROR");
}
e = p->data;
return e;
}
2.5.2.4 查找
//算法2.8 单链表的按值查找
LNode* LocateElem(LinkList L, Elemtype e)
{
//在带头结点的单链表L中查找值为e的元素
p = L->next;
while (p && p->data != e)
{
p = p->next;
}
return p;
}
2.5.2.4 C语言程序实现
//根据视频教程
#include<stdio.h>
#include<stdlib.h>
struct Node
{
int data;
struct Node* next;
};
struct Node* newNode; //不能省略
struct Node* createList();
struct Node* createNode(int data);
void insertNodeByHead(struct Node* headNode, int data);
void printList(struct Node* headNode);
struct Node* LocateElem(struct Node* headNode, int a);
int main()
{
struct Node* list = createList();
insertNodeByHead(list, 1); //从第二个位置开始存的数据
insertNodeByHead(list, 2);
insertNodeByHead(list, 3);
printList(list);
struct Node* N1;
struct Node* N2;
struct Node* N3;
N1 = LocateElem(list, 1);
N2 = LocateElem(list, 2);
N3 = LocateElem(list, 3);
printf("N1 : %d\n", N1->data);
printf("N2 : %d\n", N2->data);
printf("N3 : %d\n", N3->data);
return 0;
}
struct Node* createList()//创建链表并进行初始化(用头指针表示一个链表)
{
struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
//结构体指针headNode通过申请动态内存,产生例程,*headNode变成结构体变量
headNode->next = NULL;
return headNode;
}
struct Node* createNode(int data) //创建结点
{
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
};
//插入结点(头插法)
//参数:插入哪个链表,插入结点的数据是多少
void insertNodeByHead(struct Node* headNode, int data)
{
//1.创建要插入的结点
struct Node* newNode = createNode(data);
newNode->next = headNode->next; //不往头结点中存数据
headNode->next = newNode;
};
//遍历打印结点(不往头结点存储数据,也不打印头结点,均从第二个位置开始)
void printList(struct Node* headNode)
{
struct Node* pMove = headNode->next;
while (pMove)
{
printf("%d", pMove->data);
pMove = pMove->next;
}
printf("\n");
}
struct Node* LocateElem(struct Node* headNode,int a)
{
//在带头结点的单链表L中查找值为e的元素
struct Node* p = headNode->next;
while (p && p->data != a)
{
p = p->next;
}
return p;
}
2.5.2.5 插入
转——链表中LinkList L与LinkList *L的区别
//算法2.9 单链表的插入
Status ListInsert(LinkList &L, int i, ElemType e)
{
//在带头结点的单链表L中第i个位置插入值为e的新结点
p = L; j = 0;
while (p && (j < i - 1))
{
p = p->next;
++j;
}
if ((!p && (j != i - 1)) || (j > i - 1))
{
return ERROR;
}
s = new LNode;
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
2.5.2.5 C语言程序实现
#include <stdio.h>
#include <stdlib.h> //malloc函数
#define MAXSIZE 100
#define OK 1
typedef struct Node
{
int data;
struct Node* next;
};
struct Node* createList(); //创建单链表并进行初始化
void CreateList_R(struct Node* headNode, int n); //教材中的后插法插入结点
void printList(struct Node* headNode); //遍历打印结点
int GetElem(struct Node* headNode, int i);//取值
int ListInsert(struct Node* headNode, int length, int i, int e);//教材中的在任意位置插入一个结点
int main()
{
int e1, e2, e5,e6, e9, e10, e11 = 0;
int a1, a2, a5, a6, a9, a10, a11 = 0;
//创建单链表并进行初始化
struct Node* list = createList();
/* 教材中的头插法插入结点 */
CreateList_R(list, 10); //共存10个数且头结点中不存数据
//遍历打印结点
printList(list);
//取值
e1 = GetElem(list, 1);
e2 = GetElem(list, 2);
e5 = GetElem(list, 5);
e6 = GetElem(list, 6);
e9 = GetElem(list, 9);
e10 = GetElem(list, 10);
e11 = GetElem(list, 11);
printf("e1 : %d\ne2 : %d\ne5 : %d\ne6 : %d\ne9 : %d\ne10 :%d\ne11 :%d\n", e1, e2, e5, e6, e9, e10, e11);
printf("\n\n\n");
//在第5个位置插入数值99
ListInsert(list, 11, 5, 99);
//再次遍历打印结点
printList(list);
//再次取值并打印
a1 = GetElem(list, 1);
a2 = GetElem(list, 2);
a5 = GetElem(list, 5);
a6 = GetElem(list, 6);
a9 = GetElem(list, 9);
a10 = GetElem(list, 10);
a11 = GetElem(list, 11);
printf("a1 : %d\na2 : %d\na5 : %d\na6 : %d\na9 : %d\na10 :%d\na11 :%d\n", a1, a2, a5, a6, a9, a10, a11);
return 0;
}
//创建单链表并进行初始化
struct Node* createList()
{
struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
headNode->next = NULL;
return headNode;
}
//教材中的后插法插入结点
void CreateList_R(struct Node* headNode, int n)
{
//正位序输入n个元素的值,建立带头结点的单链表L
struct Node* r = headNode;
int i = 0;
int num = 0;
for (i = 0; i < n; ++i)
{
struct Node* p = new Node;
printf("请输入要存入的数值:");
scanf_s("%d", &num);
p->data = num;
p->next = NULL;
r->next = p;
r = p;
}
}
//遍历打印结点
void printList(struct Node* headNode)
{
int i = 0;
struct Node* pMove = headNode->next;
for (i = 1; pMove != NULL; i++)
//因为不打印数据为空的头结点,但其要算进单链表的总长度中,所以i从1开始循环增加
//若i从0开始增加,则i的值为单链表中存储的真正数据量
{
printf("%d ", pMove->data);
pMove = pMove->next;
}
printf("\nList_length : %d\n", i);
}
//取值
int GetElem(struct Node* headNode, int i)
{
struct Node* p = headNode;
int j = 1;
int e = 0;
while (p && j < i)
{
p = p->next;
++j;
}
if ((!p && (j != i)) || (j > i))
{
printf("ERROR");
}
e = p->data;
return e;
}
//教材中的在任意位置插入一个结点(必须先有一个非空链表,因此要先创建链表及结点)
//该函数与前插、后插创建链表及结点是独立的
int ListInsert(struct Node* headNode,int length, int i, int e)
{
//在带头结点的长度为length的单链表L中第i个位置插入值为e的新结点
struct Node* p = headNode;
int j = 0;
while (p && (j < i - 1))
{
p = p->next;
++j;
}
if ((!p && (j != i - 1)) || (j > i - 1))
{
printf("ERROR");
}
//加一个判断
if (length == MAXSIZE)
{
printf("ERROR");
}
struct Node* s = new Node;
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
2.5.2.6 删除
//算法2.10 单链表的删除
Status ListDelete(LinkList& L, int i)
{
//在带头结点的单链表L中,删除第i个元素
p = L; j = 0;
while ((p->next) && (j < i - 1))
{
p = p->next;
++j;
}
if (((!(p->next)) && (j != i - 1)) || (j > i - 1))
{
return ERROR;
}
q = p->next;
p->next = q->next;
delete q;
return OK;
}
2.5.2.6 C语言程序实现
//根据视频教程
#include<stdio.h>
#include<stdlib.h>
struct Node
{
int data;
struct Node* next;
};
struct Node* newNode; //不能省略
struct Node* createList();
struct Node* createNode(int data);
void insertNodeByHead(struct Node* headNode, int data);
void printList(struct Node* headNode);
void deleteNodeByAppoin(struct Node* headNode, int posData);
int main()
{
struct Node* list = createList();
newNode = createNode(10);
printf("newNode->data:%d\n", newNode->data);
printf("newNode->next:%d\n", newNode->next);
insertNodeByHead(list, 1);
insertNodeByHead(list, 2);
insertNodeByHead(list, 3);
printList(list);
deleteNodeByAppoin(list,2);
printList(list);
return 0;
}
struct Node* createList()//创建链表并进行初始化(用头指针表示一个链表)
{
struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
//结构体指针headNode通过申请动态内存,产生例程,*headNode变成结构体变量
headNode->next = NULL;
return headNode;
}
struct Node* createNode(int data) //创建结点
{
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
};
//插入结点(头插法)
//参数:插入哪个链表,插入结点的数据是多少
void insertNodeByHead(struct Node* headNode, int data)
{
//1.创建要插入的结点
struct Node* newNode = createNode(data);
newNode->next = headNode->next;
headNode->next = newNode;
};
//遍历打印结点(不往头结点存储数据,也不打印头结点,均从第二个位置开始)
void printList(struct Node* headNode)
{
struct Node* pMove = headNode->next;
while (pMove)
{
printf("%d\t", pMove->data);
pMove = pMove->next;
}
printf("\n");
}
//指定位置删除
void deleteNodeByAppoin(struct Node* headNode, int posData)
{
struct Node* posNode = headNode->next;
struct Node* posNodeFront = headNode;
if (posNode == NULL)
{
printf("无法删除,链表为空\n");
}
else
{
while (posNode->data != posData)
{
posNodeFront = posNode;
posNode = posNode->next;
if (posNode == NULL)
{
printf("没有找到相关信息,无法删除");
return;//直接结束该函数。break指退出循环。
}
}
posNodeFront->next = posNode->next;
free(posNode);
}
}
2.5.2.7 创建单链表
//算法2.11 前插法创建单链表
void CreateList_H(LinkList &L, int n)
{
//逆位序输入n个元素的值,建立带头结点的单链表L
L = new LNode;
L->next = NULL;
for (i = 0; i < n; ++i)
{
p = new LNode;
cin >> p->data;
p->next = L->next; L->next = p;
}
}
//算法2.12 后插法创建单链表
void CreateList_R(LinkList &L, int n)
{
//正位序输入n个元素的值,建立带头结点的单链表L
L = new LNode;
L->next = NULL;
r = L;
for (i = 0; i < n; ++i)
{
p = new LNode;
cin >> p->data;
p->next = NULL; r->next = p;
r = p;
}
}
2.5.2.7 前插法C语言程序实现
//根据视频教程
#include<stdio.h>
#include<stdlib.h>
struct Node
{
int data;
struct Node* next;
};
struct Node* newNode; //不能省略
struct Node* createList();
struct Node* createNode(int data);
void insertNodeByHead(struct Node* headNode, int data);
void printList(struct Node* headNode);
int main()
{
struct Node* list = createList();
newNode = createNode(10);
printf("newNode->data:%d\n", newNode->data);
printf("newNode->next:%d\n", newNode->next);
insertNodeByHead(list,1);
insertNodeByHead(list, 2);
insertNodeByHead(list, 3);
printList(list);
return 0;
}
struct Node* createList()//创建链表并进行初始化(用头指针表示一个链表)
{
struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
//结构体指针headNode通过申请动态内存,产生例程,*headNode变成结构体变量
headNode->next = NULL;
return headNode;
}
struct Node* createNode(int data) //创建结点
{
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
};
//插入结点(头插法)
//参数:插入哪个链表,插入结点的数据是多少
void insertNodeByHead(struct Node* headNode, int data)
{
//1.创建要插入的结点
struct Node* newNode = createNode(data);
newNode->next = headNode->next;
headNode->next = newNode;
};
//遍历打印结点(不往头结点存储数据,也不打印头结点,均从第二个位置开始)
void printList(struct Node* headNode)
{
struct Node* pMove = headNode->next;
while (pMove)
{
printf("%d", pMove->data);
pMove = pMove->next;
}
printf("\n");
}
2.5.2.7 后插法C语言程序实现
//根据视频教程
#include<stdio.h>
#include<stdlib.h>
struct Node
{
int data;
struct Node* next;
};
struct Node* newNode; //不能省略
struct Node* createList();
struct Node* createNode(int data);
void insertNodeByTail(struct Node* headNode, int data);
void printList(struct Node* headNode);
int main()
{
struct Node* list = createList();
insertNodeByTail(list, 1);
insertNodeByTail(list, 2);
insertNodeByTail(list, 3);
printList(list);
return 0;
}
struct Node* createList()//创建链表并进行初始化(用头指针表示一个链表)
{
struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
//结构体指针headNode通过申请动态内存,产生例程,*headNode变成结构体变量
headNode->next = NULL;
return headNode;
}
struct Node* createNode(int data) //创建结点
{
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
};
//插入结点(后插法)
//参数:插入哪个链表,插入结点的数据是多少
void insertNodeByTail(struct Node* headNode, int data)
{
struct Node* temp;
//1.创建要插入的结点
struct Node* newNode = createNode(data);
if (headNode != NULL)//不是空链表
{
temp = headNode;
printf("不是空链表\n");
while (temp->next != NULL) //定位到末尾节点
{
temp = temp->next;
}
temp->next = newNode; //末尾节点指针指向新节点
newNode->next = NULL;
}
else //是空链表
{
printf("是空链表\n");
headNode = newNode;
newNode->next = NULL;
}
}
//遍历打印结点(不往头结点存储数据,也不打印头结点,均从第二个位置开始)
void printList(struct Node* headNode)
{
struct Node* pMove = headNode->next;
while (pMove)
{
printf("%d", pMove->data);
pMove = pMove->next;
}
printf("\n");
}
2.5.3 循环链表
循环链表(CircularLinked List)是另一种形式的链式存储结构。其特点是表中最后一个结点
的指针域指向头结点,整个链表形成一个环。由此,从表中任一结点出发均可找到表中其他结点。
分为:单链的循环链表 与 多重链的循环链表。
2.5.4 双向链表
//双向链表的存储结构
typedef struct DuLNode
{
ElemType data;
struct DuLNode* prior;
struct DuLNode* next;
}DuLNode,*DuLinkList;
2.5.4.1 双向链表的插入
//算法2.13 双链表的插入
Status ListInsert_DuL(DuLinkList &L, int i, ElemType e)
{
//在带头结点的双向链表L中第1个位置之前插人元素e
if (!(p = GetElem_DuL(L, i))) //在L中确定第i个元素的位置指针P
return ERROR; //p为NULL时,第i个元素不存在
s = new DuLNode; //生成新结点 *s
s->data = e; // 将结点 *s数据域置为e
s->prior = p->prior;//将结点*s插人L中,此步对应图2.20①
p->prior->next = s; //对应图2.20②
s->next = p; //对应图2.20③
p->prior = s; //对应图2.20④
return OK;
}
2.5.4.2 双向链表的删除
//算法2.14 双向链表的删除
Status ListDelete_DuL(DuLinkList& L, int i)
{
//删除带头结点的双向链表工中的第1个元素
if (!(p = GetElem_DuL(L, i))) // 在L中确定第i个元素的位置指针P
return ERROR; // p为NULL时,第i个元素不存在
p->prior->next = p->next; //修改被删结点的前驱结点的后继指针,对应图2.21①
p->next->prior = p->prior; //修改被删结点的后继结点的前驱指针,对应图2.21②
delete p; //释放被删结点的空间
return OK;
}