线性表

来源:互联网 发布:软件需求分析培训 编辑:程序博客网 时间:2024/06/15 14:29

一、基本概念:


1.线性表:是N个数据元素的有限序列。

2.特点:
1)存在唯一一个被称为“第一个”的元素;
2)存在唯一一个被称为“最后一个”的元素;
3)除第一个元素外,每个元素都有唯一的前驱
4)除最后一个元素外,每个元素都有唯一的后继

3.分类:
1)顺序存储的线性表:物理地址的连续存储;
2)链式存储的线性表:逻辑地址的连续存储。

两种线性表的比较:数据结构 优点缺点数组通过索引可以直接访问在初始化时就需要知道元素的数量链表使用空间大小和元素成正比需要通过引用访问任意元素


二、抽象数据类型(API)

单链表API:template<typename Item>
class slist{
 slist()创建一个空链表void  push(Item item)尾结点添加一个元素Item  pop()头节点删除一个元素intsize() 链表长度bool  Empty()单链表是否为空void  trave()遍历单链表void  clear()清空单链表bool  find(Item item)查找元素是否存在void  insert(Item item,int n)插入元素(元素,位序)Item delete_1(int n)按位序删除元素void delete_2(Item item)按元素值删除元素void reverse()};单链表反序

三、代码片段

(以下为无哨兵的类型)
链表节点的构建:
结点是一个递归类型的数据结构,有两个域:
1.为可能含有任意数据类型的数据域data
2.为指向下一节结点的的指针域next
class Node{friend class slist;Item data;Node* next;};

单链表的构建:
单链表分为private和public两部分。
private内为限制访问的数据分别是:链表头结点Node* first,尾结点Node* last,
以及记录链表长度的计数器int N;
public内为可访问的数据,即为上所写的调用函数。
以及较为简单的Empty()函数和size()函数
template <typename Item>class slist{private:Node* first = NULL;Node* last = NULL;int N = 0;public:bool Empty(){return N==0;}int size(){return N;}


单链表添加元素:
这里采用的是尾结点添加法,为的是方便后续遍历等操作的进行。
主要步骤为:
1.申请新结点oldlast,指向原本last的空间;
2.last重新申请空间,存储新元素item;
3.若last为第一个进链的结点,应将first也指向该结点,
否则oldlast的next域指向last结点
4.计数器N加一。
void push(Item item){Node* oldlast = last;last = new Node();last->data = item;if (Empty())first = last;else oldlast->next = last;N++;}

单链表删除元素:
这里是从链首删除元素,需要特别注意的是考虑链为空时的情况。
步骤:
1.申请结点指针curr指向头结点,申请数据Item类型储存first的数据
2.头指针后移,删除curr;
3.计数器N减一,返回头结点元素
Item pop(){if (Empty())exit(0);//如果链表为空,结束进程Item item = first->data;Node* curr = first;first = first->next;delete curr;N--;return item;}

单链表遍历:
这里需注意不能直接移动first指针,所以需要重新申请指针结点。
步骤:
1.申请指针结点指向first
2.在指针不为空的前提下,输出每一次的data域,并将指针后移
void trave(){Node* curr = first;while (curr != NULL){cout << curr->data << " ";curr = curr->next;}cout << endl;}

清空链表:
步骤:
1.在first不为空的前提下,申请结点指针curr指向first,first指针后移
2.删除curr内数据,计数器N减一。
void clear(){while (first != NULL){Node* curr = first;first = first->next;delete curr;N--;}}

查找元素是否存在:
遍历单链表,若有相应元素输出true,否则输出false
bool find(Item item){Node* curr = first;for (int i = 0; i < N; i++)if (curr->data == item)return true;else curr = curr->next;return false;}

插入元素:
首先要考虑边界问题,防止插入元素越界
在没有哨兵的情况,头结点需单独考虑用头插法,其余用尾插法
步骤:
1.申请两个指针,一个指向插入位置的前一元素,一个指向待插入元素
2.当插入为头结点时,二指针后继指向头结点,头结点指向二指针
3.不为头结点时,用(单链表添加元素)中所用尾插法插入
(此处还得考虑尾结点插入,需要后移last指针)
4.计数器加一
void insert(string item, int n)//插入元素,插入位序{if (n<0 || n>N)exit(0);Node* curr_2 = new Node();curr_2->data = item;if (Empty())last = first=curr_2;//链表为空,构建链表else if (n==0){curr_2->next = first;first =curr_2;}else{Node* curr = first;for (int i = 0; i < n - 1; i++)curr = curr->next;curr_2->next = curr->next;curr->next = curr_2;if (n == N)last = curr_2;}N++;}



按位序删除元素:
与插入所需考虑基本相同,只需将插入步骤换为删除
中间结点删除步骤:
1.指针一寻找删除元素前一位置的元素,指针二指向删除元素;
2.指针一的后继指向指针二的后继,
3.记录指针二元素,删除指针二
string delete_1(int t)//按位置删除{if (t<0 || t>=N)exit(0);Node* curr = first;string item;if (t == 0){item = first->data;first = first->next;delete curr;}else{for (int i = 0; i < t - 1; i++)curr = curr->next;Node* curr_2 = curr->next;curr->next = curr_2->next;item = curr_2->data;if (t == N - 1)last = curr;delete curr_2;}N--;return item;}


按元素值删除元素:
同样也需要单独考虑头结点,因为可能有多个删除元素,所以这里选择构造一个哨兵。
同时一个标准变量FF记录是否删除头结点。删除其余结点的方法与上一部相同。
void delete_2(string item)//按值删除元素{Node* curr=new Node() ;curr->next = first;Node* curr_2 = first;int size = N;bool FF=0;//判断头指针是否被删除for (int i = 0; i < size; i++){if (curr_2->data == item){if (!FF)first = first->next;curr->next = curr_2->next;cout << i << " ";if (i == size-1)last = curr;delete curr_2;N--;}else{if (!FF)FF = 1;curr = curr->next;}curr_2 = curr->next;}cout << endl;}



单链表的反序:
具体步骤就是将first的后继结点依次取出,然后头插法插入链表。
void reverse()//链表反序{Node* curr = first;Node* curr_2 = first->next;while (curr_2){curr->next = curr_2->next;curr_2->next = first;first = curr_2;curr_2 = curr->next;}last = curr;}


带哨兵的单链表:
就链表而言,在插入、删除方面带哨兵要方便很多。
#include<iostream>#include<string>using namespace std;class slist{class Node{public:friend class slist;string data;Node* next;};private:Node* first=new Node();Node* last=NULL;int N=0;public:bool Empty(){ return N == 0; }int size(){ return N; }void push(string item);string pop();void trave();void clear();bool find(string item);void insert(string item, int n);string delete_1(int t);void delete_2(string item);void reverse();};void slist::push(string item){Node* oldlast = last;last = new Node();last->data = item;if(Empty())first->next= last;else oldlast->next = last;N++;}string slist::pop(){if (Empty())exit(0);Node* curr = first->next;string item = curr->data;first->next = curr->next;delete curr;N--;return item;}void slist::trave(){Node* curr = first->next;while (curr){cout << curr->data << " ";curr = curr->next;}cout << endl;}void slist::clear(){while (first->next){Node* curr = first->next;first->next = curr-> next;delete curr;N--;} }bool slist::find(string item){Node* curr = first->next;for (int i = 0; i < N; i++)if (curr->data == item)return true;else curr = curr->next;return false;}void slist::insert(string item, int n){if (n<0 || n>N)exit(0);Node* curr = first;Node* curr_2 = new Node();curr_2->data = item;for (int i = 0; i <n; i++)curr = curr->next;curr_2->next = curr->next;curr->next = curr_2;if (n == N)last = curr_2;N++;}string slist::delete_1(int t){if (t<0 || t>=N)exit(0);Node* curr = first;for (int i = 0; i <= t-1; i++)curr = curr->next;Node* curr_2 = curr->next;string item = curr_2->data;curr->next = curr_2->next;if (t == N - 1)last = curr;delete curr_2;N--;return item;}void slist::delete_2(string item){Node* curr = first;Node* curr_2 = curr->next;for (int i = 0, size = N; i < size; i++){if (curr_2->data == item){curr->next = curr_2->next;cout << i << " ";delete curr_2;N--;if (i == size - 1)last = curr;}else curr = curr->next;curr_2 = curr->next;}cout << endl;}void slist::reverse(){Node* curr = first->next;while (curr->next){Node* curr_2 = curr->next;curr->next = curr_2->next;curr_2->next = first->next;first->next = curr_2;}last = curr;}


//测试用例int main(){slist p;string item;int m, n, t;cout << "**********************************" << endl;cout << "0.查看菜单.     1.进链表." << endl;cout << "2.出链表.       3.遍历链表." << endl;cout << "4.链表是否为空.  5.链表长." << endl;cout << "6.清空链表.     7.查询值是否存在." << endl;cout << "8.插入元素.    9.按位置删除元素." << endl;cout << "10.按值删除元素. 11.链表反序." << endl;cout << "**********************************" << endl;while (cin >> m){switch (m){case 0:cout << "**********************************" << endl;cout << "0.查看菜单.     1.进链表." << endl;cout << "2.出链表.       3.遍历链表." << endl;cout << "4.链表是否为空.  5.链表长." << endl;cout << "6.清空链表.     7.查询值是否存在." << endl;cout << "8.插入元素.    9.按位置删除元素." << endl;cout << "10.按值删除元素. 11.链表反序." << endl;cout << "**********************************" << endl;break;case 1:cout << "输入进表元素:" << endl;cin >> item;p.push(item);break;case 2:cout << "出表元素为:" << p.pop() << endl;break;case 3:cout << "遍历链表:" << endl;p.trave();break;case 4:if (p.Empty())cout << "队为空" << endl;else if (!p.Empty()) cout << "队不为空" << endl;break;case 5:cout << "链表长为:" << p.size() << endl;break;case 6:cout << "清空链表." << endl;p.clear();break;case 7:cout << "输入查询值:" << endl;cin >> item;if (!p.find(item))cout << "不存在." << endl;else if (p.find(item)) cout << "存在" << endl;break;case 8:cout << "输入插入元素和位序:" << endl;cin >> item >> n;p.insert(item, n);break;case 9:cout << "输入删除元素位序:" << endl;cin >> t;cout << "删除元素为:" << p.delete_1(t) << endl;break;case 10:cout << "输入删除元素的值:" << endl;cin >> item;cout << "被删除元素的位序为:" << endl;p.delete_2(item);break;case 11:cout << "反序链表." << endl;p.reverse();break;}}system("pause");return 0;}


0 0
原创粉丝点击