【数据结构与算法】线性表

【数据结构——线性表】个人总结摘要(C++版)

目录

一、线性表的顺序存储结构——顺序表

1.顺序表的实现

(1)定义SeqList(顺序表)类

(2)输入函数

(3)输出函数

(4)按位置插入函数 

(5)按位置查找数据

(6)按位置删除数据

(7)主函数及头文件声明 

二、线性表的链接存储结构——链表 

 1.单链表的存储结构

(1)定义Node(结点)类

(2)定义LinkList(单链表)类 

(3)链表初始化

(4)析构函数-销毁单链表 

(5)链表的建立 -尾插法和头插法

(6) 链表按位置插入

(7)求链表长度 

(8)按位置查找 

(9) 查找并返回位置

(10) 按位置删除

(12)单链表的遍历-输出 

(13) 主函数及头文件声明


N个数据元素的有限序列——线性表

        线性表是一种最基本、最简单的数据结构,用来描述数据元素之间单一的前驱和后继关系。现实生活中,许多问题抽象出来的数据模型是线性表:如学籍管理问题、成绩单、通讯录等。其中约瑟夫环问题是典型的环状线性结构。
        线性表,是n个数据元素的有限序列,线性表中数据元素的个数称为线性表的长度。

图1 线性表的逻辑结构图

        对于上图中数据元素a_{0}来说,其后继为a_1{}。从图中可以看出a_{0} 无前驱,a_n{}的前驱是a_{n-1},且a_{n}无后继,a_{0}a_{n},其他数据元素均有前驱和后继。

一、线性表的顺序存储结构——顺序表

         用一段地址连续的存储单元依次存储线性表中的数据元素,顺序表存储地址是其序号的线性函数,故只要确定了基地址,计算任意一个元素的存储地址的时间是相等的,具有这一特点的存储结构称为随机存取结构注意C++语言中,数组下标是从0开始的,所以第i个元素存储在下标为i-1的位置。

1.顺序表的实现

         由于数据元素的类型不确定,所以采用C++的模板机制。(也可采用指定类型,替换掉模板即可)

(1)定义SeqList(顺序表)类

#define MAX 20    //数组长度的最大值
template<class DataType>    //函数模板:template<typename DataType>
class SeqList
{
	private:
		DataType data[MAX];
		int length;    //数组长度
	public:
		SeqList(){length=0;}    //顺序表数组空间初始化长度为0
        ~SeqList(){}    //析构函数(销毁线性表),停止调用函数时自动释放内存单元,故无须销毁
		void input();    //输入函数
		void output();    //输出函数
		void insertpos(int pos,DataType x);    //插入数据到指定位置
		DataType Get(int pos);    //按位置查找数据
		void delpos(int pos);    //按位置删除数据
};

(2)输入函数

template<class DataType>   
void SeqList<DataType>::input()    //输入数据data
{
	cout<<"input length="<<endl;
	cin>>length;
	cout<<"input "<<length<<" data:"<<endl;
	for(int i=0;i<length;i++)
	{
		cin>>data[i];
	}
}

(3)输出函数

template<class DataType>
void SeqList<DataType>::output()//输出数据data
{
	cout<<"output "<<length<<" data:"<<endl;
	for(int i=0;i<length;i++)
	{
		cout<<data[i]<<" ";
	}
}

(4)按位置插入函数 

template<class DataType>
void SeqList<DataType>::insertpos(int pos,DataType x)    //插入数据data到第pos位置
{
	if(length==MAX) throw"memory is full";
	if(pos<1 || pos>length+1) throw"insert pos is error";//序号是从1开始,插入位置小于1则插入失败,大于数组长度也插入失败
	int i;
	for(i=length-1;i>pos-1;i--)    //从最后一个数据开始,往后移动一位
	{
		data[i+1]=data[i];    //把前一个数据赋值给后一个
	}
	data[i+1]=x;    //上面for循环结束时,i时第pos-1的位置,故i+1才是pos要插入的位置
	length++;    //插入一个元素数组长度+1
}

(5)按位置查找数据

template<class DataType>
DataType SeqList<DataType>::Get(int pos)    //按位置查找数据
{
	if(pos<1 || pos>length) throw"pos is error";
		return data[pos-1];    //返回下标为pos-1的数据
}

(6)按位置删除数据

template<class DataType>
void SeqList<DataType>::delpos(int pos)    //按位置删除一个数据
{
	if(pos<1 || pos>length) throw"pos is error";
	for(int i=pos-1;i<length-1;i++)    //下标为pos-1的数据就是要删除的数据
	{
		data[i]=data[i+1];    //从pos-1开始,后面的数据前移
	}
	length--;    //删除一个数据,数组长度length-1
}

(7)主函数及头文件声明 

#include<iostream>
using namespace std;
//各函数、类插入此处
int main()
{
	SeqList<int> l;
	l.input();
	l.output();
	int pos1;
	//按位置插入数据
	cout<<" input insert pos1,x"<<endl;
	int x;
	cin>>pos1>>x;
	try//抛出异常
	{
		l.insertpos(pos1,x);
		l.output();
	}
	catch(char *str)
	{
		cout<<str<<endl;
	}
	//按位置查找数据
    int pos2;
	cout<<"\ninput get pos2 ";
	cin>>pos2;
	try
	{
		cout<<l.Get(pos2)<<endl;
		l.output();
	}
	catch(char *str)
	{
		cout<<str<<endl;
	}
	//按位置删除数据
    int pos3;
	cout<<"\ninput delpos ";
	cin>>pos3;
	try
	{
		l.delpos(pos3);
		l.output();
	}
	catch(char *str)
	{
		cout<<str<<endl;
	}
	return 0;
}//若想运行全部代码只需把各代码段拼接即可

二、线性表的链接存储结构——链表 

 1.单链表的存储结构

        单链表时用一组任意的存储单元存放线性表的元素,这组存储单元可以连续也可以不连续,甚至可以零散分布在内存中的任意位置。
        为了能正确表示元素之间的逻辑关系,每个存储单元在存储数据元素的同时,还必须存储其后继元素所在的地址信息(单链表顺序存取结构)。如下图:

图3 单链表的结点结构
图2 线性表(a1,a2,a3)的单链表存储

(1)定义Node(结点)类

template <typename DataType>
struct Node             //结点定义
{
	DataType data;
	Node<DataType> *next;    //指针next
};

(2)定义LinkList(单链表)类 

template<class DataType>
class Linklist
{
public:
	Linklist();    //链表初始化为只有一个头指针的单链表		
	~Linklist();    //销毁单链表
	Linklist(DataType a[], int n);    //链表初始化为有n个数据的单链表		
	void Insert(int pos, DataType x);    //链表按位置插入	
	int Size();	//链表长度	
	DataType Get(int pos);	    //按位置查找,并返回			
	int Locate(DataType x);	    //按数据定位			
	DataType Delete(int pos);    //按位置删除
	void sort_list();    //单链表排序
	void output();	    //单链表遍历			
private:
	Node<DataType>* first;//定义一个头指针
};

 (3)链表初始化

template<class DataType>
Linklist<DataType>::Linklist()
{
	first = new Node<DataType>;    //申请动态空间
	first->next = NULL;            //头指针的指针域指向空
}

(4)析构函数-销毁单链表 

template<class DataType>
Linklist<DataType>::~Linklist()
{
	Node<DataType>* p = first;    //指向单链表的头指针
	while (p!=NULL)
	{
		Node<DataType>* t = p;    //t指向p
		delete t;                   //删除指针t
		p = p->next;            //p向后走
	}
}

(5)链表的建立 -尾插法和头插法

1)尾插法:
        1.1 新建一个指针endl指向first
              然后再初始化一个指针temp,并赋空
        1.2 在for循环中
              在temp的数据域存放需要插入的数据元素
              endl->next指向temp
              末尾指针跟着temp走,即endl始终在最后一个位置,endl=temp
        1.3 结束循环后,endl指针域置空(特别注意的是,在链表中,为了防止访问到没有数据的结点,链表指针应注意指向是否为空)

template<class DataType>
Linklist<DataType>::Linklist(DataType a[], int n)//尾插法建立链表
{
    first=new Node<DataType>;
	Node<DataType>* endl=first,* temp=NULL;
	for (int i = 0; i < n; i++)
	{
		temp = new Node<DataType>;
		temp->data = a[i];
		endl->next = temp;
		endl = temp;
	}
	endl->next = NULL;
}

2)头插法
        2.1 新建一个结点p,始终指向p的next
        2.2 建立一个temp
        2.3 在for循环中
              在temp的数据域存放需要插入的数据元素
              temp的下一个插入到头指针的后面,即p的前面(链接时应先使temp的next指向first的next,即p,然后使头指针的next指向temp)此时即完成了temp的插入操作
              p应继续指向first的next
由此可见,头插法建立的数据是逆序的

first = new Node<DataType>;
first->next = NULL;
Node<DataType>* p = first->next, * temp;
for (int i = 0; i < n; i++)
{
	temp = new Node<DataType>;
	temp->data = a[i];
	temp->next = p;
	first->next = temp;
	p = first->next;
}

(6) 链表按位置插入

template<class DataType>
void Linklist<DataType>::Insert(int pos, DataType x)
{
	Node<DataType>* p = first, * s = NULL;
	int count = 0;
	while (p != NULL && count < pos - 1)
	{
		p = p->next;
		count++;
	}
	if (p == NULL) throw"插入位置错误";
	else
	{
		s = new Node<DataType>;
		s->data = x;
		s->next = p->next;
		p->next = s;
	}
}

(7)求链表长度 

        较为简单,可以计数器遍历累加计数,或者返回n(但若对链表进行插入或者删除则不能直接返回n).

template<class DataType>
int Linklist<DataType>::Size()
{
	Node<DataType>* p = first->next;
	int length = 0;
	while (p != NULL)
	{
		p = p->next;
		length++;
	}
	return length;
}

(8)按位置查找 

template<class DataType>
DataType Linklist<DataType>::Get(int pos)
{
	Node<DataType>* p = first->next;
	int count = 0;
	while (p != NULL && count < pos - 1)
	{
		p = p->next;
		count++;
	}
	if (p == NULL) throw"pos is error";
	else return p->data;
}

(9) 查找并返回位置

template<class DataType>
int Linklist<DataType>::Locate(DataType x)
{
	Node<DataType>* p = first->next;
	int count = 1;
	while (p != NULL)
	{
		if (p->data == x) return count;
		p = p->next;
		count++;
	}
	return 0;
}

(10) 按位置删除

template<class DataType>
DataType Linklist<DataType>::Delete(int pos)
{
	DataType x;
	Node<DataType>* p = first, * q = NULL;
	int count = 0;
	while (p != NULL && count < pos - 1)
	{
		p = p->next;
		count++;
	}
	if (p == NULL || p->next == NULL) throw"delpos is error";
	else
	{
		q = p->next; x = q->data;
		p->next = q->next;
		delete q;
		return x;
	}
}

(11) 单链表排序

        本段代码采用冒泡排序的方法(关于冒泡排序日后更新后可在主页查找)

template<class DataType>
void Linklist<DataType>::sort_list()
{
	Node<DataType>* p = first->next, * s, * temp;
	s = new Node<DataType>;
	s = p->next;
	while (p->next)//p走到最后一个,然后结束排序;
	{
		while (s)
		{
			if (p->data > s->data)
			{
				temp = new Node<DataType>;
				temp->data = p->data;
				p->data = s->data;
				s->data = temp->data;
			}
			else s = s->next;
		}
		p = p->next;
		s = p->next;
	}
}

(12)单链表的遍历-输出 

template<class DataType>
void Linklist<DataType>::output()
{
	Node<DataType>* p = first->next;
	while (p != NULL)
	{
		cout << p->data << " ";
		p = p->next;
	}
	cout << endl;
}

(13) 主函数及头文件声明

#include<iostream>
using namespace std;

//此处插入各函数或类

int main()
{
	int a[20], i, n;
	cout << "input n: ";
	cin >> n;
	for (i = 0; i < n; i++)
	{
		cin >> a[i];
	}
	Linklist<int> l(a, n);
	int inpos, x;
	cout << "input insert pos,x: ";
	cin >> inpos >> x;
	try
	{
		l.Insert(inpos, x);
		cout << "output inserted data: ";
		l.output();
	}
	catch (char* str) { cout << str << endl; }
	cout << "Linklist length: " << l.Size() << endl;
	int getpos;
	cout << "input getpos: ";
	cin >> getpos;
	try
	{
		cout << "output Get data: " << l.Get(getpos) << endl;
	}
	catch (char* str) { cout << str << endl; }
	int y;
	cout << "input Locate data: ";
	cin >> y;
	try
	{
		cout << "output Locate num: " << l.Locate(y) << endl;
	}
	catch (char* str) { cout << str << endl; }
	int delpos;
	cout << "input delpos: ";
	cin >> delpos;
	try
	{
		cout << "output delpos data: " << l.Delete(delpos) << endl;
		l.output();
	}
	catch (char* str) { cout << str << endl; }
	l.sort_list();
	cout << "sort the Linklist:" << endl;
	l.output();
	return 0;
}

        小伙伴们,因为我本身也在学习数据结构中,此系列文章也作为笔记记录分享给大家,如有错误希望各位指正,我会继续努力,提高自己水平。
注:
        参考书籍《数据结构—— 从概念到C++实现(第三版)》,作者王红梅等。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悦享未来

你的鼓励必将成为我前进的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值