线性表

一、基本概念:


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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值