第二章 线性表

  大家好,我叫亓官劼(qí guān jié )


第二章 线性表 线性表的链式表示和实现_结点


2.3 线性表的链式表示和实现

由于顺序表的插入、删除操作需要移动大量的元素,影响运行效率,因此引入了线性表的链式存储。链式存储线性表时,不需要使用地址连续的存储单元,即它不要求逻辑上相邻的两个元素在物理位置上也相邻,这里使用指针来表示逻辑间的关系。

2.3.1 单链表的定义

线性表的链式存储又称为单链表,它是指通过一组任意的存储单元来存储线性表种的数据元素。为了建立数据元素之间的线性关系,除了存储元素自身的信息之外,还存放一个指向其后继节点的指针。

单链表的结点类型定义为:

// 单链表存储结点的结构
typedef struct LNode {
ElemType data;// 数据域
struct LNode* next;//指针域
}LNode,*LinkList;

利用单链表可以解决顺序表需要大量连续存储空间的缺点。但是单链表需要存储指针域,也会浪费存储空间。单链表的增加、删除操作的效率比顺序表要高。由于单链表中的元素是离散的分布在存储空间中,所以单链表是非随机存取的存储结构。

带头结点的优点:

  • 链表中第一个位置上的操作和其他位置上的操作一致,无须进行特殊处理。
  • 空链表和非空链表的处理一致,无须进行特殊处理。

2.3.2 单链表基本操作的实现

下面来实现一些单链表的操作:

(1)采用头插法建立单链表。

该方法从一个空表开始,生成新结点,并将读取到的数据存放到新结点的数据域中,然后将新结点存放到链表的表头,直到输入特殊的数之后停止。

代码实现为:

// 采用头插法创建链表
LinkList List_HeadInsert(LinkList& L) {
LNode *s;
int x;
// 为L分配内存空间
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
cin >> x;
while (x != 6666) {// x不为6666时,一致进行头插法创建新链表
s = (LinkList)malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
cin >> x;
}
return L;//返回新创建的L
}

(2)采用尾插法建立单链表

该方法从一个空表开始,生成新结点,并将读取到的数据存放到新结点的数据域中,然后将新结点存放到链表的表尾,直到输入特殊的数之后停止。

代码实现为:

// 采用尾插法创建链表
LinkList List_TailInsert(LinkList& L) {
LNode* s;
LNode* p;//用来记录位置
int x;
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
p = L;
cin >> x;
while (x != 6666) {//直到输入6666停止插入
s = (LinkList)malloc(sizeof(LNode));
s->data = x;
s->next = NULL;
p->next = s;
p = p->next;// p=s亦可
cin >> x;
}
return L;// 返回创建好的链表
}

(3)按序号查找结点

// 按序号查找结点
LNode* GetElem(LinkList L, int i) {
LNode* p = L->next;
int j = 1;
if (i == 0) // i = 0时,序号为0的结点为头节点,直接返回
return L;
if (i < 1) // 非法输入,返回NULL
return NULL;
while (p && j < i){// 从序号为1的结点开始移动,直到移动到第i个结点或者链表结束
j++;
p = p->next;
}
// 移动完之后直接返回p,当访问到第i的结点时,p指向的位置就是第i个结点,
//如果链表中不存在第i个结点,则会移动到链表结束,p的值为NULL
return p;
}

(4)按值查找结点

// 按值查找结点
LNode* LocateElem(LinkList L, ElemType e) {
LNode* p = L->next;
while (p && p->data!=e){
p = p->next;
}
return p;
}

(5)插入结点操作(头插法)

// 插入结点操作 头插法
bool InsertHLinst(LinkList& L, int i, ElemType e) {
// 在第i个位置插入值为e的结点
LNode* p = L->next;
LNode* s = (LinkList)malloc(sizeof(LNode));
if (!s) {
return false;//分配失败
}
s->data = e;
int j = 1;
// 将p指针移动到第i-1个结点的位置
while (p && j < i - 1) {
j++;
p = p->next;
}
s->next = p->next;
p->next = s;
return true;
}

(6)删除结点操作

// 删除结点
bool DeleteList(LinkList& L, int i) {
// 删除第i个结点
if (i < 1)
return false;
LNode* p = L->next;
int j = 1;
// 移动p指针到i-1个结点
while (p && j < i-1) {
p = p->next;
j++;
}
if (j != i - 1)
return false;//没有第i个结点
LNode* s = p->next;
p->next = s->next;
free(s);
return true;
}

(7)求表长操作

// 求表长
int Length(LinkList L) {
int n = 0;
LNode* p = L->next;
while (p)
{
p = p->next;
n++;
}
return n;
}

(8)输出链表

// 输出链表
void PrintList(LinkList L) {
cout << "当前链表为:";
LNode* p = L->next;
while (p)
{
cout << p->data << " ";
p = p->next;
}
cout << endl;
}

2.3.3 单链表测试用例

完整的测试程序为:

#include<iostream>
#include<cstdlib>
using namespace std;
typedef int ElemType;
// 单链表存储结点的结构
typedef struct LNode {
ElemType data;// 数据域
struct LNode* next;//指针域
}LNode,*LinkList;

// 采用头插法创建链表
LinkList List_HeadInsert(LinkList& L) {
LNode *s;
int x;
// 为L分配内存空间
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
cin >> x;
while (x != 6666) {// x不为6666时,一致进行头插法创建新链表
s = (LinkList)malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
cin >> x;
}
return L;//返回新创建的L
}

// 采用尾插法创建链表
LinkList List_TailInsert(LinkList& L) {
LNode* s;
LNode* p;//用来记录位置
int x;
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
p = L;
cin >> x;
while (x != 6666) {//直到输入6666停止插入
s = (LinkList)malloc(sizeof(LNode));
s->data = x;
s->next = NULL;
p->next = s;
p = p->next;// p=s亦可
cin >> x;
}
return L;// 返回创建好的链表
}

// 按序号查找结点
LNode* GetElem(LinkList L, int i) {
LNode* p = L->next;
int j = 1;
if (i == 0) // i = 0时,序号为0的结点为头节点,直接返回
return L;
if (i < 1) // 非法输入,返回NULL
return NULL;
while (p && j < i){// 从序号为1的结点开始移动,直到移动到第i个结点或者链表结束
j++;
p = p->next;
}
// 移动完之后直接返回p,当访问到第i的结点时,p指向的位置就是第i个结点,
//如果链表中不存在第i个结点,则会移动到链表结束,p的值为NULL
return p;
}

// 按值查找结点
LNode* LocateElem(LinkList L, ElemType e) {
LNode* p = L->next;
while (p && p->data!=e){
p = p->next;
}
return p;
}

// 插入结点操作 头插法
bool InsertHLinst(LinkList& L, int i, ElemType e) {
// 在第i个位置插入值为e的结点
LNode* p = L->next;
LNode* s = (LinkList)malloc(sizeof(LNode));
if (!s) {
return false;//分配失败
}
s->data = e;
int j = 1;
// 将p指针移动到第i-1个结点的位置
while (p && j < i - 1) {
j++;
p = p->next;
}
s->next = p->next;
p->next = s;
return true;
}

// 删除结点
bool DeleteList(LinkList& L, int i) {
// 删除第i个结点
if (i < 1)
return false;
LNode* p = L->next;
int j = 1;
// 移动p指针到i-1个结点
while (p && j < i-1) {
p = p->next;
j++;
}
if (j != i - 1)
return false;//没有第i个结点
LNode* s = p->next;
p->next = s->next;
free(s);
return true;
}

// 求表长
int Length(LinkList L) {
int n = 0;
LNode* p = L->next;
while (p)
{
p = p->next;
n++;
}
return n;
}

// 输出链表
void PrintList(LinkList L) {
cout << "当前链表为:";
LNode* p = L->next;
while (p)
{
cout << p->data << " ";
p = p->next;
}
cout << endl;
}

int main() {
LinkList LA, LB;
cout << "采用头插法创建链表LA,直至输入6666停止插入" << endl;
List_HeadInsert(LA);
cout << "插入成功!" << endl << "LA链表为:";
PrintList(LA);
cout << "当前LA的表长为" << Length(LA) << endl;
cout << "采用尾插法创建链表LB,直至输入6666停止插入" << endl;
List_TailInsert(LB);
cout << "插入成功!" << endl << "LB链表为:";
PrintList(LB);
cout << "当前LB的表长为" << Length(LB) << endl;
cout << "查找LA中值为6元素的位置为:" << LocateElem(LA, 6) << endl;
cout << "LA中第二个元素的值为:" << GetElem(LA, 2)->data << endl;
cout << "在LA中第二个位置插入一个值为5555的结点!" << endl;
InsertHLinst(LA, 2, 5555);
cout << "插入后的LA为:" << endl;
PrintList(LA);
cout << "删除LA的第4个结点!" << endl;
DeleteList(LA, 4);
cout << "删除后的LA为:" << endl;
PrintList(LA);
return 0;
}

程序输出为:

采用头插法创建链表LA,直至输入6666停止插入
1 2 3 4 5 6 7 8 9 0
6666
插入成功!
LA链表为:当前链表为:0 9 8 7 6 5 4 3 2 1
当前LA的表长为10
采用尾插法创建链表LB,直至输入6666停止插入
1 2 3 4 5 6 7 8 9 0 6666
插入成功!
LB链表为:当前链表为:1 2 3 4 5 6 7 8 9 0
当前LB的表长为10
查找LA中值为6元素的位置为:00BE1070
LA中第二个元素的值为:9
在LA中第二个位置插入一个值为5555的结点!
插入后的LA为:
当前链表为:0 5555 9 8 7 6 5 4 3 2 1
删除LA的第4个结点!
删除后的LA为:
当前链表为:0 5555 9 7 6 5 4 3 2 1

  大家好,我叫亓官劼(qí guān jié )