【数据结构】链表

本文详细介绍了链表的基本定义,包括数据部分和指针部分,并探讨了链表的两种存储方式:静态链表和动态链表。接着,文章深入讲解了单向链表的运算实现,包括插入、删除操作,并给出了应用实例和代码示例。此外,还讨论了单循环链表、带尾指针的单循环链表以及双向链表的结构和特点。

1.线性表

2.顺序表

基本定义

链表的定义:链表(linked list)结构是由若干个具有相同结构形式的、称为结点的变量用链地址连接而成的结构形式。其中,每个结点逻辑上由两部分组成的,即数据部分(data)和指示后继结点的变量或者是指针(next)。

链表的术语:指向表头指示起点的指针叫头指针(head)。称存储表的首元素a₁的结点为首元素结点。最后一个结点(尾节点)的后继指针为空值,表示其后续没有结点了。

存储方式

静态链表

如果用数组中的元素来存储元素的值和地址,则可以构成存储在数组中的链表。由于在程序运行中,数组一直存在,且其元素个数固定不变,因而称这种链表为静态链表。

定义如下:

template <class Typename>
struct node {
	Typename data;
	int next;
};
node<int> list[8];

动态链表

在许多情况下,由于事先难以估计存储结构中元素的个数,因此静态链表难以适用。在这种情况下,希望在程序的运行中,根据实际问题的需要临时、动态地分配存储空间,这就需要动态链表。动态链表中的每个结点就是在运行中所产生的动态变量。

定义如下:

template <class Typename>
struct node {
	Typename data;
	node *next;
};

运算实现

单向链表

运算实现

为了使插入、删除元素时的操作一致,增加一个头结点。

头文件:

#ifndef LIST_H
#define LIST_H

template <class Type>
struct node {
	Type data;
	node *next;
};

template <class Type>
class list {
public:
	list();//构造函数初始化(C++11可以在类中初始化)
	~list();//析构函数释放链表所有空间
	void creat_list();
	int length();//求链表长度
	void get_element(const int i, Type &x);//按序号取元素
	node<Type>* locate(const Type x);//按元素求地址
	void insert(const int i, const Type x);//按序号插入元素
	void delete_element(const int i);//按序号删除元素
	node<Type>* get_head();//读取头结点指针
	void show();//打印链表元素
	void copy(list B);//复制B表的内容
private:
	node<Type> *head;
};

template <class Type>
list<Type>::list() {
	head = new node<Type>;
	head->next = NULL;
}
template <class Type>
void list<Type>::creat_list() {
	int x;
	node<Type> *p = head;
	cout << "输入序列:";
	while (cin >> x) {
		node<Type> *u = new node<Type>;
		u->data = x;
		u->next = NULL;
		p->next = u;
		p = p->next;
	}
	cin.clear();
	cout << "创建列表成功!" << endl;
}
template <class Type>
int list<Type>::length() {
	int count = 0;
	if (head->next != NULL) {
		node<Type> *u = head->next;
		while (u != NULL) {
			++count;
			u = u->next;
		}
	}
	return count;
}
template <class Type>
void list<Type>::get_element(const int i, Type &x) {
	if (i > 0 && i < lenth() + 1) {
		node<Type> *u = head->next;
		for (int j = 1; j < i; ++j)
			u = u->next;
		x = u->data;
	}
}
template <class Type>
node<Type>* list<Type>::locate(const Type x) {
	node<Type> *u = head->next;
	while (u->data != x&&u != NULL)
		u = u->next;
	return u;
}
template <class Type>
void list<Type>::insert(const int i, const Type x) {
	if (i > 0 && i < length() + 1) {
		node<Type> *u = new node<Type>;
		node<Type> *v = head;
		for (int j = 1; j < i; ++j)
			v = v->next;
		u->data = x;
		u->next = v->next;
		v->next = u;
	}
}
template <class Type>
void list<Type>::delete_element(const int i) {
	if (i > 0 && i < length() + 1) {
		node<Type> *u = head;
		for (int j = 1; j < i; ++j)
			u = u->next;
		u->next = u->next->next;
		delete u->next;
	}
}
template <class Type>
node<Type>* list<Type>::get_head() {
	return head;
}
template <class Type>
void list<Type>::show() {
	if (length()) {
		cout << "序列:";
		node<Type> *u = head->next;
		while (u != NULL) {
			cout << u->data << " ";
			u = u->next;
		}
		cout << endl;
	}
	else cout << "空链表!" << endl;
}
template <class Type>
void list<Type>::copy(list B) {
	node<Type> *pa = head;
	node<Type> *pb = B.get_head()->next;
	while (pa->next != NULL)
		pa = pa->next;
	while (pb != NULL) {
		node<Type> *u = new node<Type>;
		u->data = pb->data;
		pa->next = u;
		pa = pa->next;
		pb = pb->next;
	}
	pa->next = NULL;
	cout << "复制成功!" << endl;
}
template <class Type>
list<Type>::~list() {
	for (int i = length(); i > 0; --i)
		delete_element(i);
	delete head;
}

#endif

应用实例

设计算法将链表B复制到链表A

源代码:

#include<iostream>
#include"list.h"
using namespace std;

int main() {
	int x;
	list<int> A, B;
	A.creat_list();
	B.creat_list();
	A.copy(B);
	A.show();
	cin.get();
}

运行截图

  

单循环链表

如果将单向链表的表尾结点中的后继指针改为指向表头结点,就构成了单循环链表。

单循环链表可以不带头结点,其特点是可从任意元素结点出发搜索到其他各元素结点。

带尾指针的单循环链表

在许多情况下,要求链表能方便地搜索到表头和表尾结点。为此,可采用带尾指针的单循环链表结构。这类结构也可以不带头结点。

双向链表

如果希望能快速地求出任一链表结点的前驱和后继指针,则需要用到双链表结构。双链表中每个结点除了后继指针外,还增加了指向其前驱的指针。双链表也可以带头结点,也可以是循环的。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值