2.数据结构C++学习笔记——线性表(链式储存)

此篇为本人自己学习过程中的笔记,有许多不足之处,仅供参考。

目录

链式表

1. 定义与介绍

2. 有关术语

3. 链表存储结构的特点

4. 以C++模版类实现链式存储

4.1 链表类与节点类的定义

4.2 判断链表是否为空

4.3 单链表的销毁

4.4 清空单链表

4.5 求表长

4.6 在链表的头部或者尾部插入节点建立单链表

4.6.1头插法

 4.6.2尾插法

4.7   取值  取链表中第i个元素的内容

4.8 查找

  4.8.1 按值查找——根据指定数据获取该数据的位置(地址)

  4.8.2 按值查找——根据指定数据获取该数据的位置序号

4.9 插入

4.10 删除


链式表

1. 定义与介绍

有关线性表的定义上一篇已经介绍,所以直接介绍链式储存的特点

(2条消息) 1.数据结构C++学习笔记——线性表(顺序储存)_偏爱晚风_ly的博客-CSDN博客

概念:线性表的链式表示,又称为非顺序映像,或链式映像

●  用一组物理位置任意的储存单元,来存放线性表的数据元素。

●  这组存储单元即可以是连续的,也可以是不连续的,甚至是零散分布在内存的各个位置上

  逻辑上相邻的数据元素,在物理上不一定相邻。

2. 有关术语

链表:n个节结点由指针域组成一个链表。
结点:数据元素的储存映像,由数据域和指针域组成。
数据域:存储数据元素本身。
指针域:储存直接后继结点的储存位置。最后一个元素的指针域为空。
头结点:在首元结点之前附加的一个节点。头结点的数据域为可以空,也可以存放数据表长度等附加信息,但此结点不能计入链表长度值。
头指针:指向链表中第一个节点的指针。单链表由头指针唯一确定,因此单链表可以用头指针的名字来命名。
首元节点:链表中储存第一个数据元素的节点。

2.数据结构C++学习笔记——线性表(链式储存)_第1张图片

图2.1 指针节点相关示意图


单链表:结点只有一个指针域的链表,也叫线性链表。

2.数据结构C++学习笔记——线性表(链式储存)_第2张图片

图2.2  带头节点的单链表示意图


双链表:结点有两个指针域的链表。

2.数据结构C++学习笔记——线性表(链式储存)_第3张图片

图2.3 带头节点的双链表示意图


循环链表:首尾相连的链表。

2.数据结构C++学习笔记——线性表(链式储存)_第4张图片

 图2.4 带头节点的循环链表示意图


3. 链表存储结构的特点

(1)结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻。

(2)访问时只能通过头指针进入链表,并通过每个结点的指针域依次向后顺序扫描其余结点,所以寻找第一个结点和最后一个结点所花费的时间不等。这种存取元素的方法称为顺序存储法。我们之前学过的顺序表,是随机储存法,时间复杂度为O_{1}

讨论1——如何表示空表

无头结点时,头指针为空的表为空表。
有头结点时,头结点的指针域为空,则表示空表。

讨论2——在链表中设置头结点有什么好处

1. 便于首元结点的处理,首元结点的地址保存在头结点的指针域中,所以在链表第一个位置的操作,与其他位置一样,不需要进行特殊处理,
2. 便于空表与非空表的统一处理,无论链表是否为空,头指针都是指向头结点的非空指针,因此,空表与非空表的处理也就统一了。

4. 以C++模版类实现链式存储

4.1 链表类与节点类的定义


template
class LinkList; //先声明,方便引用
template
class Node
{
	friend class LinkList; //设为友元
public:
	T data;           //数据域
	Node * next;   //指针域
};

template
class LinkList
{
public:
	LinkList();      // 构造函数
	~LinkList();     // 析构函数
	void AddtoHead(); //头插法
	void AddToTail(); //尾插法
	void IsEmpty();  //判断链表是否为空
	void Destroy();  //销毁链表
	void Clear();    //清空链表
	int Length();    //求单链表表长

public:
	Node* head;//头指针
	Node* tail;//尾指针
	int length; //单链表长度
};

4.2 判断链表是否为空


template
void LinkList::IsEmpty()
{
	if (!head)
	{
		cout<<"链表不存在" << endl;
    }
	else if (head->next == NULL)
    {
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

4.3 单链表的销毁

从头指针开始依次释放所有节点

template
void LinkList::Destroy()
{
	Node* p;  //创建一个临时指针
	while (p != NULL)  
	{
		p = head;  //head头指针中存放头节点的地址,指针p指向头节点
		head = head->next;  //关键步骤,将head向后移,指向下一个结点
		delete p;           //释放p
	}
}

4.4 清空单链表

链表仍然存在,但是链表中无元素 (头指针,头结点仍然在),从首元结点依次释放所有结点,并将头结点的指针域置空。

2.数据结构C++学习笔记——线性表(链式储存)_第5张图片

2.数据结构C++学习笔记——线性表(链式储存)_第6张图片

template
void LinkList::Clear()
{
	Node* p,*q;  //创建临时指针

		p = head->next;  //p指针指向首元节点
	while (p==NULL)
    {
		q = p->next;     //q指针指向p指针的下一个结点
		delete p;        //释放p
		p = q;           //再令p指针和q指针指向同一结点
	}
	head->next = NULL;     //头节点的指针域置空
}

4.5 求表长

从首元节点开始,依次计数所有结点

template
int LinkList::Length()
{
	Node* p;  //创建临时指针
	p = head->next;  //p指针指向首元节点
	int i = 0;    // i为计数变量
	while (p == NULL)
	{
		i++;
		p = p->next;
	}
	return i;
}

4.6 在链表的头部或者尾部插入节点建立单链表

4.6.1头插法

       链表是一种动态管理的储存结构,链表中每个节点占用的储存空间不是预先分配的,而是运行时系统根据需求生成的。因此,建立单链表从空表开始,每次读入一个数据元素(结点)就申请一次指针,然后插在链表的头部,因为是从头部插入,所以读入数据元素的顺序与线性表中的逻辑顺序是相反的。算法的时间复杂度为O_{(n)}

template
void LinkList::AddtoHead()
{
	Node* newNode;  // newNode 是一个指针,类型为Node
	int x;
	cin >> x;      //输入要插入的元素值x
	while (x != FLAG)   //x=FLAG时候退出循环
	{
		newNode = new Node;   //新申请节点,newNode指向该节点
		newNode->data = x;       //将x存放到新节点中的数据域
		newNode->next = head->next;   //头节点的指针域赋值给新节点的指针域
		head->next = newNode;   //将新节点插入到链表前端
		cin >> x;
	}
}

 4.6.2尾插法

       头插入建立单链表简单,但读入的数据元素的顺序与生成的链表中元素的顺序是相反的,若希望顺序一致,则用尾插入的方法。因为每次是将新结点插入到链表的尾部,所以用法尾指针始终指向链表中的尾结点,以便能够将新结点插入到链表的尾部。

template
void LinkList::AddToTail()
{
	Node* newNode;  // newNode 是一个指针,类型为Node
	int x;
	cin >> x;      //输入要插入的元素值x
	this->tail = this->head;   //尾指针指向头节点
	while (x != FLAG)   //x=FLAG时候退出循环
	{
		newNode = new Node;   //新申请节点,newNode指向该节点
		newNode->data = x;       //将x存放到新节点中的数据域
		tail->next = newNode;  // 插入到表尾
		tail = newNode;    //tail指向新的尾结点
		cin >>x;     //继续输入要插入的元素
	}

}

4.7   取值  取链表中第i个元素的内容

       从链表的头指针出发,顺着链域next逐个结点往下搜索,直至搜索到第i个结点为止。也因此,链表不是随机存储结构。

T LinkList::GetElem(int i)
{
	Node* p = this->head->next;  //p指向首元节点
	int j = 0;  //初始化
	while (p && p == NULL)  //向后扫描,直到p指向第i个元素或p为空
	{
		p = p->next;
		j++;
	}
	if (!p || j > i) //第i个元素不存在
	{
		return FALSE; 
	}
	else
	{
		return p->data;   //取第i个元素
	}
}

4.8 查找

  4.8.1 按值查找——根据指定数据获取该数据的位置(地址)

从第一个结点开始,依次与e进行比较,如果找到一个其值与e相等的数据元素,则返回其在链表中的位置或地址,如果查遍整个链表都没有找到其值和e相等的元素,则返回0或NULL。

template
Node* LinkList::LocateElem(T e)
{
	Node* p = this->head->next;  //p指向首元节点
	while (p&&p->data!=e)
	{
		p = p->next;
		return p;
	}
}

  4.8.2 按值查找——根据指定数据获取该数据的位置序号

template
int LinkList::LocateElem_L(T e)
{
	Node* p = this->head->next;  //p指向首元节点
	int j = 1;
	while (p && p->data != e)
	{
			p = p->next;
			j++;
	}
	if (p)
	{
		return j;
	}
	else
	{
		return 0;
	}
}

4.9 插入

       在第i个结点前插入值为e的新节点,首先找到ai-1的储存位置p,生成一个数据域为e的新节点S插入新的节点:新节点的指针域指向ai,结点ai-1的指针域指向新节点。

template
void LinkList::ListInsert(int i, T e)
{
	Node* p = this->head;
	int j = 0;
	if (p && j > i - 1)
	{
		return FALSE;
	}
	while (p && j < i - 1) //寻找第i-1个结点,p指向i-1结点
	{
		p = p->next;
		j++;
	}
	 Node s;    //生成新节点
	s->data = e;         //将e放在新节点的数据域中
	s->next = p->next;   //将结点插入L中
	p->next = s; 
}

4.10 删除

        删除第i个节点,首先找到ai-1的储存位置p,保存要删除的ai的值。令p->next指向ai+1。释放节点ai的空间。

template
void LinkList::ListDelete(int i)
{
	Node* p = this->head;
	int j = 0;
	if (!(p->next)||j>i-1)
	{
		cout << "删除位置不合理" << endl;
		return;
	}
	while (p->next && j < i - 1)  //寻找第i个节点
	{
		p = p->next;  //p指向前驱
		j++;
	}
	Node *q = p->next;
	p->next = q->next;
	delete q;
}

你可能感兴趣的:(数据结构C++学习笔记,笔记,数据结构,c++,算法,链表)