DS005数据结构-单链表实现原理探究-C++模板类实现

一、顺序表那么好,为什么还需要链表

顺序表的缺点:

顺序 的除了表尾外其他位置插入 和删除元素需要移动其他元素,效率低 ;平均时间复杂度为 O(n)
顺序表需要连续的空间,当数据量比较大时,如果没有足够的连续空间,顺序表失效 !!
 
为解决该缺点,设计了链表。 链表的插入和删除不需要移动其他元素,插入删除效率高;但失去了随机存取的优势
 
另:这是一种颠覆性创新,另起炉灶,不在顺序存储的框架内修修补补!!
单链表是最简单的链式结构,由此发散、引申,可以构造更复杂的结构,比如树、图。
基础不牢,地动山摇,必须把链表学好。

二、单链表的定义

单链表是通过在当前结点中存放后继结点的地址将序列数据像“链”一样连接起来,所以称为链表。
单链表的“单”指的是当前结点中只存放一个地址数据,即后继数据结点的地址。

三、单链表的结点形态

四、单链表的整体形态

在单链表中设立头结点,是为了方便在链表的头部插入和删除结点。

      本节我们讲的单链表的头结点和数据结点是相同类型。

       有时候为了方便,头结点中可以设一个指向链表头的指针、一个指向链表尾的指针和一个表示链表长度的整数。这时候头结点的类型就和链表结点的类型就不同了。

五、链表结点的C++描述

template<typename T>class list
{//单向链表 
    struct node
    {
        T data;
        node* next;
    };
    node* head;//头结点指针
    int length;//记录元素结点个数

}

这样,head就是头指针,length记录链表中元素结点的个数,不包括头结点。

六、生成一个结点    

	node* creatNode(T x)
	{//为x生成结点,返回结点地址
		node* t;
		t = new node;
		if (t == 0)
			return 0;
		t->data = x;
		t->next = 0;
		return t;

	}

用new运算符,向操作系统申请node类型的空间。

若操作系统说,我也不富裕,拒绝了,则返回0,即空指针,否则返回新结点的地址。 

七、初始化

开始时,生成一个头节点,在构造函数中实现。

	void init()
	{
		head = new node;
		head->next = 0;
		length = 0;
	}

    list()
	{
		init();
	}

head是一个变量,里面存的是地址,是头结点的地址,这个头结点没有名字。 

八、在某个结点后面插入元素

void insert(node* p, node* q)
	{//p是链表中某个结点的指针,q是一个新结点的指针
		//将q插入到p的后面
		q->next = p->next;
		p->next = q;
		length++;
	}

	void insert(node* p, T x)
	{//在指针p所指的结点后面插入x
		node* q, * t;

		t = creatNode(x);
		insert(p, t);		

	}

C++的函数重载,函数名相同,参数不同。 

在结点p后面插入q结点,一定不能先断开p后面的指针,断开前,一定要有指针指向原来p后面的结点,

不然就会丢失结点。

九、在头结点后面插入元素

void push_front(T x)
	{

		insert(head, x);

	}

有了前面 insert 函数的铺垫, 在头部插入结点的操作就是如此简单。

十、在链表尾部插入结点

void push_back(T x)
	{
		node* p;
		p = head;
		while (p->next)
		{
			p = p->next;
		}

		insert(p, x);

	}

 先找到链表的尾部结点,用指针p指向它,然后再p后面插入数据。

十一、得到第i个结点的地址

node* Address(int i)
	{//得到第i个元素结点的地址
		if (i<0 || i>length)
			return 0;
		if (i == 0)
			return head;
		if (i == 1)
			return head->next;


		int j = 0;
		node* p = head;
		while (j < i)
		{
			p = p->next;
			j++;
		}
		return p;

	}

在单链表中找到第 i 个结点,是一个基本操作。 

十二、在第i个结点前插入数据

void insert(int i, T x)
	{//在链表的第i个数据前面插入x
	 //即在第i-1个元素后面插入x
		node* p;
		if (i == 1)
			push_front(x);
		else if (i == length + 1)
			push_back(x);
		else
		{
			p = Address(i - 1);
			insert(p, x);

		}

	}

要在第 i 个结点处插入数据,就是在第 i-1 个结点后插入数据,需要先找到第 i-1 个结点。

十三、删除第i个结点

void erase(int i)
	{//删除第i个元素结点
		if (i<1 || i>length)
			return;
		node* q, * t;
		t = Address(i - 1);
		q = t->next;
		t->next = q->next;

		delete q;
		length--;
	}

要删除第 i 个结点,需要先找到第 i-1 个结点。 

十四、在链表中查找

node* find(T x)
	{//查找元素x,若存在返回地址,否则返回空指针0
		node* p = head->next;
		while (p != 0)
		{
			if (p->data == x)
				return p;
			p = p->next;
		}
		return 0;
	}

十五、释放单链表占用的空间

在析构函数中实现

~list()
	{
		node* p;
		node* q;
		while (head->next)
		{
			p = head->next;
			head->next = p->next;
			delete p;
		}
		delete head;
		head = 0;
		length = 0;
	}

十六、完整代码



//#include "stdafx.h" //vc下面工程带的头文件,用dev编译时注释掉即可


#include <iostream>
using namespace std;

template<typename T>class list
{//单向链表 
	struct node
	{
		T data;
		node* next;
	};
	node* head;//头结点
	int length;
public:
	void init()
	{
		head = new node;
		head->next = 0;
		length = 0;
	}
	list()
	{
		init();
	}
	int size()
	{
		return length;
	}
	void print()
	{
		node* p;
		p = head->next;
		while (p != 0)
		{
			cout << p->data << " ";
			p = p->next;
		}
		cout << endl;
	}
	node* creatNode(T x)
	{//为x生成结点,返回结点地址
		node* t;
		t = new node;
		t->data = x;
		t->next = 0;
		return t;

	}

	void insert(node* p, node* q)
	{//p是链表中某个结点的指针,q是一个新结点的指针
		//将q插入到p的后面
		q->next = p->next;
		p->next = q;
		length++;
	}

	void insert(node* p, T x)
	{//在指针p所指的结点后面插入x
		node* q, * t;

		t = creatNode(x);
		insert(p, t);		

	}

	



	void push_front(T x)
	{

		insert(head, x);

	}
	void push_back(T x)
	{
		node* p;
		p = head;
		while (p->next)
		{
			p = p->next;
		}

		insert(p, x);

	}
	node* Address(int i)
	{//得到第i个元素结点的地址
		if (i<0 || i>length)
			return 0;
		if (i == 0)
			return head;
		if (i == 1)
			return head->next;


		int j = 0;
		node* p = head;
		while (j < i)
		{
			p = p->next;
			j++;
		}
		return p;

	}

	void insert(int i, T x)
	{//在链表的第i个数据前面插入x
	 //即在第i-1个元素后面插入x
		node* p;
		if (i == 1)
			push_front(x);
		else if (i == length + 1)
			push_back(x);
		else
		{
			p = Address(i - 1);
			insert(p, x);

		}

	}
	
	void erase(int i)
	{//删除第i个元素结点
		if (i<1 || i>length)
			return;
		node* q, * t;
		t = Address(i - 1);
		q = t->next;
		t->next = q->next;

		delete q;
		length--;
	}

	node* find(T x)
	{
		node* p = head->next;
		while (p != 0)
		{
			if (p->data == x)
				return p;
			p = p->next;
		}
		return 0;
	}

	~list()
	{
		node* p;
		node* q;
		while (head->next)
		{
			p = head->next;
			head->next = p->next;
			delete p;
		}
		delete head;
		head = 0;
		length = 0;
	}
};

int main()
{
	list<int> L;
	L.push_front(3);
	L.push_front(2);
	L.push_front(1);

	L.print();

	L.push_back(4);
	L.push_back(5);
	L.push_back(6);
	L.print();

	cout << L.size() << endl;

	cout << (L.Address(6)->data) << endl;

	L.insert(1, 44);
	L.print();
	L.insert(3, 55);
	L.print();
	L.insert(8, 66);
	L.print();

	cout << "begin to delete:\n";
	L.erase(1);
	L.print();
	L.erase(L.size());
	L.print();
	L.erase(3);
	L.print();

	cout << L.find(55)->data << endl;


	return 0;
}

十七、运行结果

猜你喜欢

转载自blog.csdn.net/weixin_43917370/article/details/108565818