【数据结构——线性表】个人总结摘要(C++版)
目录
N个数据元素的有限序列——线性表
线性表是一种最基本、最简单的数据结构,用来描述数据元素之间单一的前驱和后继关系。现实生活中,许多问题抽象出来的数据模型是线性表:如学籍管理问题、成绩单、通讯录等。其中约瑟夫环问题是典型的环状线性结构。
线性表,是n个数据元素的有限序列,线性表中数据元素的个数称为线性表的长度。
![](https://i-blog.csdnimg.cn/blog_migrate/369be5055b8ec618e5238b17ba7b992e.png)
对于上图中数据元素来说,其后继为
。从图中可以看出
无前驱,
的前驱是
,且
无后继,除
,
外,其他数据元素均有前驱和后继。
一、线性表的顺序存储结构——顺序表
用一段地址连续的存储单元依次存储线性表中的数据元素,顺序表存储地址是其序号的线性函数,故只要确定了基地址,计算任意一个元素的存储地址的时间是相等的,具有这一特点的存储结构称为随机存取结构。注意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.单链表的存储结构
单链表时用一组任意的存储单元存放线性表的元素,这组存储单元可以连续也可以不连续,甚至可以零散分布在内存中的任意位置。
为了能正确表示元素之间的逻辑关系,每个存储单元在存储数据元素的同时,还必须存储其后继元素所在的地址信息(单链表是顺序存取结构)。如下图:
![](https://i-blog.csdnimg.cn/blog_migrate/7666372bda3e4f1ec9808c9919008f34.png)
![](https://i-blog.csdnimg.cn/blog_migrate/474281216de0d2ea94443c909c0d3ed7.png)
(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++实现(第三版)》,作者王红梅等。