【数据结构】C++实现之单线性表

1、概念
详见https://blog.csdn.net/qq_30611601/article/details/79516986
线性表是最基本、最简单、也是最常用的一种数据结构。线性表是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的**(注意,这句话适用大部分线性表,而不是全部)**
在这里插入图片描述

2、线性表常用操作

线性表的操作包括如下几种
(1InitList(&L)		初始化,构造一个空的线性表
(2ListEmpty(L)	判断线性表是否为空,true or flase
(3ClearList(&L)	清空线性表中的内容
(4GetElem(L,i,&e)	返回线性表i位置上的元素值,通过e返回
(5LocateElem(L,e)		在线性表中找到与e相同的元素,成功则返回其序号,否则返回0表示失败
(9Listinsert(&L,i,e)	如果线性表存在了,而且i符合条件,则在i位置插入一个元素e
(10ListDelete(*L,i,*e)	删除i位置上的元素,并用e返回其值
(5ListLength(L)	返回线性表的长度

3、顺序存储结构线性表

3.1、线性表的顺序存储结构
1)顺序表示,其实就是数组,连续内存空间
2)优点:
随机访问特性,查找O(1)时间,存储密度高;
逻辑上相邻的元素,物理上也相邻;
无须为表中元素之间的逻辑关系而增加额外的存储空间;
3)缺点:
插入和删除需移动大量元素;
当线性表长度变化较大时,难以确定存储空间的容量;
造成存储空间的“碎片
在这里插入图片描述

#define  MAXSIZE 20		//存储空间初始分配量
typedef int Elemtype;   //Elemtype数据类型根据实时而定,这里假设为int
typedef int Status;

struct List {
    
    
		/*	data:   元素数据存储空间
			length: 线性表长度(变动的),从1开始
			MAXSIZE: 线性表最大长度,即数组长度(不变) */
	Elemtype data[MAXSIZE];
	int length;					
};

3.2、顺序结构的获取元素操作
1)当为空线性表时,ERROR抛出
2)返回位置异常时,ERROR抛出

//返回线性表i位置上的元素值,通过e返回
Status GetElem(List L, int i, Elemtype *e)
{
    
    

	if (L.length == 0 || i<1 || i>L.length)
		return ERROR;

	*e = L.data[i - 1];
	///*为什么是i-1,这是由于我们平时以及线性表都是下标从1开
	//始的,而在C++中数组索引是从0开始,所以我们要对应减1
	return OK;
}

5、顺序结构的插入操作
1)保证线表不是空表
2)插入位置不合理,抛出异常
3)表的长度达到最大容量,插不进
4)特殊情况处理:直接插入表尾,与非表尾情况
5)插入数据后,后面的数据要往后移
【注意:该插入时将值插入到 i 之前】
在这里插入图片描述

//插值到线性表操作
Status Listinsert(List *L, int i, Elemtype e) //线性表,插入表位置,插入元素值e
{
    
     
	/*插入元素到第I位置之前*/
	int k;
	if (L->length == MAXSIZE)  //表满,插不进
		return ERROR;
	if (i<1 || i>L->length + 1) //插入位置不合理
		return ERROR;
	if (i <= L->length)   //插入位置不为表尾
	{
    
    
		for (k = L->length - 1; k >= i - 1; k--)
			L->data[k+1] = L->data[k];
	}
	L->data[i-1] = e;   //插入目标值
	L->length++;	//更新线性表长度
	return OK;		//完成标志
}

3.3、顺序结构的删除操作
1)删除位置不合理,抛出异常
2)考虑删除位置不是最后位置与是最后位置情况
3)删除后,后面数据前移动1个
在这里插入图片描述


//删除
Status ListDelete(List *L, int i, Elemtype *e) //表,位置,删除元素值
{
    
    
	if (L->length == 0)     //空表,异常抛出
		return ERROR;
	if (i > L->length || i < 1)  //删除位置不合理,异常抛出
		return ERROR;
	*e = L->data[i - 1];     //删除目标元素
	if (i < L->length)      //删除位置非表尾
	{
    
    
		for (int k = i; k < L->length ; k++)
		{
    
    
			L->data[k - 1] = L->data[k];
		}
	}
	L->length--;
	return OK;
}

3.4、顺序结构线性表完整代码

/*
作者:kimicr
时间:20200406
功能:c++实现之顺序结构线性表
版本:VS2013
*/
#include"iostream"
#include"stdlib.h"
#include"stdio.h"
#include"time.h"
#include"List.h"
using namespace std;
#define ERROR 0
#define OK 1

/*
线性表的操作包括如下几种
(1) InitList(*L)		初始化,构造一个空的线性表
(2) ListEmpty(L)	判断线性表是否为空,true or flase
(3) ClearList(*L)	清空线性表中的内容
(4) GetElem(L,i,*e)	返回线性表i位置上的元素值,通过e返回
(5) LocateElem(L,e)		在线性表中找到与e相同的元素,成功则返回其序号,否则返回0表示失败
(6) Listinsert(*L,i,e)	如果线性表存在了,而且i符合条件,则在i位置插入一个元素e
(7) ListDelete(*L,i,*e)	删除i位置上的元素,并用e返回其值
(8) ListLength(L)	返回线性表的长度
*/


//顺序存储线性表
#define  MAXSIZE 20		//存储空间初始分配量
typedef int Elemtype;   //Elemtype数据类型根据实时而定,这里假设为int
typedef int Status;

struct List {
    
    
		/*	data:   元素数据存储空间
			length: 线性表长度(变动的),从1开始
			MAXSIZE: 线性表最大长度,即数组长度(不变) */
	Elemtype data[MAXSIZE];
	int length;					
};


//返回线性表i位置上的元素值,通过e返回
Status GetElem(List L, int i, Elemtype *e)
{
    
    

	if (L.length == 0 || i<1 || i>L.length)
		return ERROR;

	*e = L.data[i - 1];
	///*为什么是i-1,这是由于我们平时以及线性表都是下标从1开
	//始的,而在C++中数组索引是从0开始,所以我们要对应减1
	return OK;
	
}


//插值到线性表操作
Status Listinsert(List *L, int i, Elemtype e) //线性表,插入表位置,插入元素值e
{
    
     
	/*插入元素到第I位置之前*/
	int k;
	if (L->length == MAXSIZE)  //表满,插不进
		return ERROR;
	if (i<1 || i>L->length + 1) //插入位置不合理
		return ERROR;
	if (i <= L->length)   //插入位置不为表尾
	{
    
    
		for (k = L->length - 1; k >= i - 1; k--)
			L->data[k+1] = L->data[k];
	}
	L->data[i-1] = e;   //插入目标值
	L->length++;	//更新线性表长度
	return OK;		//完成标志
}


//删除
Status ListDelete(List *L, int i, Elemtype *e) //表,位置,删除元素值
{
    
    
	if (L->length == 0)     //空表,异常抛出
		return ERROR;
	if (i > L->length || i < 1)  //删除位置不合理,异常抛出
		return ERROR;
	*e = L->data[i - 1];     //删除目标元素
	if (i < L->length)      //删除位置非表尾
	{
    
    
		for (int k = i; k < L->length ; k++)
		{
    
    
			L->data[k - 1] = L->data[k];
		}
	}
	L->length--;
	return OK;
}
	
int main()
{
    
    
	List list1;
	list1.data[0] = 50;
	list1.length = 1;	//初始化线性表
	Listinsert(&list1, 1, 1);
	Listinsert(&list1, 1, 2);
	Listinsert(&list1, 1, 3);

	//遍历并输出该链表上的所有数据
	cout << "遍历并输出该链表上的所有数据:" << endl;
	for (int i = 0; i <list1.length ; i++)
		cout << list1.data[i] << " ";
	cout << endl;

	int e=0;
	ListDelete(&list1, 2,&e);     //删除元素
	//遍历并输出该链表上的所有数据
	cout << "遍历并输出该链表上的所有数据:" << endl;
	for (int i = 0; i < list1.length; i++)
		cout << list1.data[i] << " ";
	cout << endl;
	system("pause");
	return 0;
}
遍历并输出该链表上的所有数据:
3 2 1 50
遍历并输出该链表上的所有数据:
3 1 50
请按任意键继续. . .

4、链式结构线性表

4.1、链式结构
1)链式存储结构,分散的内存空间
2)优点
插入、删除不需移动其他元素,只需改变指针.
链表各个节点在内存中空间不要求连续,空间利用率高
元素理论上可以无限个,不需要预分配内存
3)缺点
查找需要遍历操作,比较麻烦
在这里插入图片描述

struct Node
{
    
    
	Elemtype data;              //存储数据
	Node *next;       //存储下一个结点的地址
};
typedef Node *LinkList;     //定义指针数据类型Node

4.2、单链表的读取(查找)
1)声明一个指向链表的指针P,j=1开始计数
2)j<i,不断遍历,让指针p后移
3)当p指向为空时,即链表找完时,则表明第i元素不存在
4)否则查找成功

//取值 
Status GetElem(LinkList L, int i, Elemtype *e)
{
    
    
	int j;
	LinkList p;			//声明一个指向链表结构的指针 
	p = L->next;		//初始化指针,指向链表L的第一个节点
	j = 1;				//j为计数器
	while ( p&& j < i)  //*p不为空以及j还没有大于等于i时,继续循环
	{
    
    
		p = p->next;	  //指针指向下一节点
		++j;			 //计数器加一
	}
	if (!p || j>i)		//第i元素不存在时,抛出异常
		return ERROR;
	*e = p->data;		//返回第i值
	return OK;
}

4.3、单链表的插入
1)需要插入目标链表,位置,数值
2)创建两个指针,分别指向链表与新元素地址
3)j=1遍历计数,找到插入位置,如p=NULL表示链表中没有,反之则分配空间生成一个新的节点(new实现)
4)赋值以及更改i-1,与新节点的指针域值
在这里插入图片描述
在这里插入图片描述

//插入第i位置之前
Status Listinsert(LinkList *L, int i, Elemtype e)
{
    
    
	int j;					//计数器
	LinkList p;
	p = *L;					//指向链表
	j = 1;					//计数器
	while (p&&j < i)		 //遍历找到插入新节点的目标位置
	{
    
    
		p = p->next;
		++j;
	}           
	if (!p || j>i)
		return ERROR;      //链表中不存在第i个元素

	Node *s = new Node;     //生成的新节点
	s->data = e;            //更新节点的数值data
	s->next = p->next;		//将p指向的下一节作为给s节点的下一节点
	p->next = s;			//将s地址作为p的下一节点
	//注意:这里不要释放s,因为是插入节点
	return OK;
}

4.4、单链表的删除
1)需要目标链表,删除位置
2)建立两个指针p,q,分别用来指向链表与要删除节点地址
3)遍历,如P=null,没有这个元素,否者将该元素地址
4)完成删除,并回收节点空间delete q;
在这里插入图片描述
4.5 、单链表的整表创建
1)声明节点指针P以及计数器count
2)初始化一空链表L
3)让L头指针指向Null,建立一个带头节点的单链表
4)循环,分配空间生成新节点
5)完成创建
【注意:头插法,即使让新的节点总是在第一位】
在这里插入图片描述

//单链表的整表创建

void CreateListHead(LinkList *L, int n)
{
    
    
	/*随机产生n个元素值,建立代表头节点的单链线性表L*/
	/*插队方式,始终让新产生节点处于第1节点【头插法】*/

	LinkList p;
	srand(time(0));       //初始化随机数种子
	*L = new Node;
	(*L)->next = NULL;    //先建立一个带头节点的单链表
	for (int i = 0; i < n; i++)
	{
    
    
		p = new Node;   //生成新节
		p->data = rand() % 100 + 1;   //随机生成100以内的数
		p->next = (*L)->next; 
		(*L)->next = p;         //插入到表头
	}
	
}

在这里插入图片描述
【还有尾插法:即使将每次新产生的节点总是处于最后一位】

*插队方式,始终让新产生节点处于末尾【尾插法】*/
void CreateListTail(LinkList *L, int n)
{
    
    
	LinkList p,r;
	srand(time(0));       //初始化随机数种子
	*L = new Node;
	r = *L;
	for (int i = 0; i < n; i++)
	{
    
    
		Node *p = new Node;   //生成新节
		p->data = rand() % 100 + 1;   //随机生成100以内的数
		r->next = p;         //插入到表头
		r = p;
	}
	r->next = NULL;

}

4.7、单链表的整表删除
1)需两个节点指针P,Q,作用分别是指向下一节点以及释放当前节点

Status ClearList(LinkList *L)
{
    
    
	/*初始条件,线性表已经存在 、目标:清空线性表*/
	LinkList p, q;
	p = (*L)->next;
	while (p)
	{
    
    
		q = p->next;
		delete p;
		p = q;
	}
	(*L)->next = NULL;    //头节点指针域为空
	return OK;
}

【链表 VS 顺序存储结构线性表】
在这里插入图片描述
4.8 链表完全代码
注意:LinkLink *L; 是一个二重指针

/*
作者:kimicr
时间:20200406
功能:c++实现之顺序结构线性表
版本:VS2013
*/
typedef int Elemtype;
typedef int Status;

struct Node
{
    
    
	Elemtype data;              //存储数据
	Node *next;       //存储下一个结点的地址
};
typedef Node *LinkList;     //定义新的数据类型-指向结构体的指针


//单链表的整表创建
//LinkLink *L;是一个二重指针

void CreateListHead(LinkList *L, int n)
{
    
    
	/*随机产生n个元素值,建立代表头节点的单链线性表L*/
	/*插队方式,始终让新产生节点处于第1节点【头插法】*/

	LinkList p;
	srand(time(0));       //初始化随机数种子
	*L = new Node;
	(*L)->next = NULL;    //先建立一个带头节点的单链表
	for (int i = 0; i < n; i++)
	{
    
    
		p = new Node;   //生成新节
		p->data = rand() % 100 + 1;   //随机生成100以内的数
		p->next = (*L)->next; 
		(*L)->next = p;         //插入到表头
	}
	
}



/*插队方式,始终让新产生节点处于末尾【尾插法】*/
void CreateListTail(LinkList *L, int n)
{
    
    
	LinkList p,r;
	srand(time(0));       //初始化随机数种子
	*L = new Node;
	r = *L;
	for (int i = 0; i < n; i++)
	{
    
    
		Node *p = new Node;   //生成新节
		p->data = rand() % 100 + 1;   //随机生成100以内的数
		r->next = p;         //插入到表头
		r = p;
	}
	r->next = NULL;

}


//取值 
Status GetElem(LinkList L, int i, Elemtype *e)
{
    
    
	int j;
	LinkList p;			//声明一个指向链表结构的指针 
	p = L->next;		//初始化指针,指向链表L的第一个节点
	j = 1;				//j为计数器
	while ( p&& j < i)  //*p不为空以及j还没有大于等于i时,继续循环
	{
    
    
		p = p->next;	  //指针指向下一节点
		++j;			 //计数器加一
	}
	if (!p || j>i)		//第i元素不存在时,抛出异常
		return ERROR;
	*e = p->data;		//返回第i值
	return OK;
}


//插入第i位置之前
Status Listinsert(LinkList *L, int i, Elemtype e)
{
    
    
	int j;					//计数器
	LinkList p;
	p = *L;					//指向链表
	j = 1;					//计数器
	while (p&&j < i)		 //遍历找到插入新节点的目标位置
	{
    
    
		p = p->next;
		++j;
	}           
	if (!p || j>i)
		return ERROR;      //链表中不存在第i个元素

	Node *s = new Node;     //生成的新节点
	s->data = e;            //更新节点的数值data
	s->next = p->next;		//将p指向的下一节作为给s节点的下一节点
	p->next = s;			//将s地址作为p的下一节点
	//注意:这里不要释放s,因为是插入节点
	return OK;
}



//删除
Status ListDelete(LinkList *L, int i, int *e)
{
    
    
	int j=1;
	LinkList p,q;
	p = *L;
	while (p->next&&j < i)   //遍历寻找第i元素,当j=i时便会跳出循环,此时p->next是第i-1节点地址。p此时指向的时i-1节点
	{
    
    
		p = p->next;
		++j;
	}
	if (!p || j>i)
		return ERROR;		//第i元素不存在
	q = p->next;		//现在将p后继节点地址赋给q,让q来指向第i节点
	p->next = q->next;  //将q指向i节点中的后继节点(i+1)地址赋给p的指针域
	*e = p->data;
	delete q;         //让系统回收一个Node节点空间,释放内存
	return OK;

}

Status ClearList(LinkList *L)
{
    
    
	/*初始条件,线性表已经存在 、目标:清空线性表*/
	LinkList p, q;
	p = (*L)->next;
	while (p)
	{
    
    
		q = p->next;
		delete p;
		p = q;
	}
	(*L)->next = NULL;    //头节点指针域为空
	return OK;
}


int main()
{
    
    
	LinkList list2;              //创建一个指向指向结构体的指针
	CreateListHead(&list2, 10);

	Listinsert(&list2, 1, 999);
	Listinsert(&list2, 1, 1000);
	Listinsert(&list2, 1, 1200);

	//遍历并输出该链表上的所有数据
	cout << "遍历并输出该链表上的所有数据:" << endl;
	LinkList p,q;
	p = list2->next;
	while (p)
	{
    
    
		cout << p->data<<" ";
		q = p->next;
		p = q;
	}
	cout << endl;

	int e = 0;
	ListDelete(&list2, 2,&e);


	//遍历并输出该链表上的所有数据
	cout << "遍历并输出该链表上的所有数据:" << endl;
	p = list2->next;
	while (p)
	{
    
    
		cout << p->data << " ";
		q = p->next;
		p = q;
	}
	cout << endl;
	delete list2;
	system("pause");
	return 0;
}
结果输出:
遍历并输出该链表上的所有数据:
1200 1000 999 3 72 69 72 77 3 92 61 8 36
遍历并输出该链表上的所有数据:
1200 999 3 72 69 72 77 3 92 61 8 36
请按任意键继续. . .

猜你喜欢

转载自blog.csdn.net/qq_32643313/article/details/105311262