顺序表与链表

目录

一.顺序表

1.顺序表的声明

2. 退出功能

3.初始化和析构

 4.插入功能

5.查找功能

6.删除功能

7.顺序表的优缺点

二.链表

1.声明

2.链表的初始化

3.插入功能

4.查找功能

5.删除功能

6.输出功能

7.带表头节点的单链表

(1)初始化

 (2)插入

 (3)删除

8.单循环链表

9.双向链表

(1)插入

(2)删除


一.顺序表

利用C++来实现顺序表

1.顺序表的声明

#include<iostream>
using namespace std;
#define Type int 
#define OK 1
#define NO 0
#define Size 20//顺序表的容量大小
#define Num 2//顺序表的元素个数

//操作顺序表的类
class orderTable
{
private:

	int m_size;//顺序表的最大长度

	Type* m_table;//用来维护指向顺序表的指针

	int m_number;//顺序表的元素个数

public:

	//退出
	void Exit();

	//顺序表的初始化
	 orderTable(int size);

	 //析构
	 ~orderTable();

	 //顺序表的插入
	 int Insert(int n,int x);

	 //顺序表的查找
	 int Find(int n);

	 //顺序表的删除
	 int Delete(int n);

};

2. 退出功能

//退出
void orderTable::Exit()
{
	exit(0);
}

3.初始化和析构

//顺序表的初始化
 orderTable::orderTable(int size)
{
	//初始化元素个数
	this->m_number =Num;

	//初始化最大长度
	this->m_size = size;

	//在堆内存创建顺序表
	this->m_table = new int(size);
}
//析构
 orderTable::~orderTable()
 {
	 if (this->m_table != NULL)
	 {
		 delete m_table;
		 m_table = NULL;
	 }
 }

堆区空间需要在析构中释放 

 4.插入功能

//顺序表的插入
 //n为元素下标,x为元素
 int orderTable::Insert(int n,int x)
 {
	 //判断顺序表是否已满
	 if (this->m_number == this->m_size)
	 {
		 cout << "顺序表已满,请重新操作" << endl;
		 return NO;
	 }

	 //判断顺序表插入位置是否合理
	 if (n<-1 || n>this->m_number - 1)
	 {
		 //不是小于0而是小于-1是因为不仅可以在后面插入,也可以在前面插入
		 cout << "插入元素下标错误,请重新操作" << endl;
		 return NO;
	 }

	 //将插入的元素后面的元素全部往后移
     //从最后一个元素开始才能保证后移过程元素不被覆盖
	 for (int j = this->m_number - 1; j > n; j--)
	 {
		 this->m_table[j + 1] = this->m_table[j];
	 }

	 //将元素插入

	 this->m_table[n + 1] = x;

	 cout << this->m_table[n + 1] << endl;

	 //将元素个数加一
	 this->m_number++;

	 return OK;

 }

每一次插入都要做是否越界检查,顺序表插入算法的平均时间复杂度为O(n) 

5.查找功能

 //查找
 int orderTable::Find(int n)
 {
	 if (n<-1 || n>this->m_number - 1)
	 {
		 cout << "下标输入错误,请重新输入" << endl;
		 return NO;
	 }

	 cout << this->m_table[n] << endl;
	 return OK;
 }

6.删除功能

//删除
 int orderTable::Delete(int n)
 {
	 if (this->m_number == 0)
	 {
		 cout << "顺序表为空" << endl;
		 return NO;
	 }

	 if (n<-1 || n>this->m_number - 1)
	 {
		 cout << "下标输入错误,请重新输入" << endl;
		 return NO;
	 }

	 //将要删除的元素被后面的元素替代
	 for (int j = n; j < this->m_number - 2; j++)
	 {
		 this->m_table[j] = this->m_table[j + 1];
	 }

	 this->m_number--;

	 return OK;
 }

顺序表的删除先要判断是否为空,删除下标是否越界,然后采用逻辑删除(即直接覆盖)删除

删除算法的平均时间复杂度为O(n)

7.顺序表的优缺点

1.顺序表较于链表可以直接查找到想要的元素,即直接m_table[n] 访问,而链表需要通过指针域一个一个找,则到找到,所以顺序表的查找时间复杂度为O(1),而链表为O(n)n为查找下标

2.顺序表的插入和删除都要移动大量的元素,时间复杂度为O(n),而链表通过指针直接删除和插入,时间复杂度为O(1),所以当需要大量删除和插入操作时链表效率更高


二.链表

1.声明

#include<iostream>
using namespace std;
#define OK 1
#define NO 0
#define Type int

class List
{
public:
	//数据域
	Type element;

	//指针域
	List* m_link;
};

class singleList
{
public:
	//头指针
	List* first;

	//链表元素个数
	int n;
};

//管理类
class Manager
{
public:
	//初始化链表
	int Init(singleList& L);

	//查找
	int Find(int x, singleList& L);

	//插入
	int Insert(int x, Type date, singleList& L);

	//删除
	int Delete(int x, singleList& L);

	//输出
	int outPut(singleList& L);
};

由于链表需要用到头指针,所以需要两个类头指针first来指向链表内的元素(即指向链表的类)

链表具有数据域和指针域,该元素的指针域指向链表里的下一个元素,所以指针域的指针指向自身

对类的传递都使用引用传递,既简单又对类本身操作(不需要复制类的副本)

2.链表的初始化

//初始化
int Manager::Init(singleList& L)
{
	L.first = NULL;
	L.n = 0;
	return OK;
}

 初始化只需要对类sigleList进行操作,不需要对链表的数据域和指针域初始化,因为元素个数n为0

3.插入功能

//插入(x为插入的下标,date为插入的元素)
int Manager::Insert(int x, Type date, singleList& L)
{
	List* p;
	p = L.first;//需要两个指针,头指针移动到被插入节点的位置,新建一个指针来指向插入元素
	List* q;//将新建指针作为插入元素的指针域,所以直接指向链表类

	//插入
	//先找到插入下标的指针域
	for (int i = 0; i < x; i++)
	{
		p = p->m_link;
	}
	//创建一个新的节点
	q = new(List);//新节点的指针域
	q->element = date;//新节点的数据域

	//插入在p指向的节点后
	if (x > -1)
	{
		q->m_link = p->m_link;
		p->m_link = q;
	}
	//插入在头节点前成为新的头节点
	else
	{
		q->m_link = L.first;
		L.first = q;
	}
	L.n++;
	return OK;

}

链表的插入需要注意两种情况,插入的是链表头节点后还是头节点前,头节点前则需要作为新的头节点

4.查找功能

//查找(x为查找元素的下标)
int Manager::Find(int x, singleList& L)
{
	if (x<0 || x>L.n-1)
	{
		return NO;
	}

	List* p;
	p = L.first;

	//由p进行一个节点一个节点查找
	for (int i = 0; i < x; i++)
	{
		p = p->m_link;
	}

	Type date = p->element;
	return date;
}

查找只需要注意是否越界

5.删除功能

//删除
int Manager::Delete(int x, singleList& L)
{
	if (L.n==0)
	{
		return NO;
	}

	List* p;
	p = L.first;
	List* q;
	q = L.first;//两个指针都从头节点开始移动,分别找到各自相应位置,链表必须要两个指针操作

	//找到删除的节点的前一个节点
	for (int i = 0; i < x-1; i++)
	{
		p = p->m_link;
	}
	//找到删除的节点
	for (int j = 0; j < x; j++)
	{
		q = q->m_link;
	}

	//删除的是头节点
	if (x == 0)
	{
		L.first = L.first->m_link;//头节点的指针域指向下一个值,将头指针指向下一个值
	}
	else
	{
		p->m_link = q->m_link;//直接将被删元素指针域赋给前一个节点,前一个节点直接指向被删元素
                              //下一个节点,即逻辑上的删除
		
	}
	delete q;
	L.n--;
	
	return OK;

}

6.输出功能

//输出
int Manager::outPut(singleList& L)
{
	List* p;
	p = L.first;

	if (L.n == 0)
	{
		cout << "链表为空" << endl;
	}
	else
	{
		for (int i = 0; i < L.n - 1; i++)
		{
			cout << p->element << " ";
			p = p->m_link;

		}
	}
	return OK;
}

7.带表头节点的单链表

带表头节点的链表可以不用考虑插入和删除时是头节点的情况,具有更高效率

(1)初始化

class List
{
public:
	//数据域
	Type element;

	//指针域
	List* m_link;
};

class singleList
{
public:
	//表头指针
	List* head;

	//链表元素个数
	int n;
};

//初始化
int Manager::Init(singleList& L)
{
    L.head=new(list);
	L.head->m_list=NULL;//表头节点的数据域不存放元素,所以不需要置为空
                        //指针域指向下一个元素,则需要置为空
	L.n = 0;
	return OK;
}

 (2)插入

//插入(x为插入的下标,date为插入的元素)
int Manager::Insert(int x, Type date, singleList& L)
{
	List* p;
	p = L.head;
	List* q;

	//插入
	//先找到插入下标的指针域
	for (int i = 0; i <= x; i++)//表头节点的插入和头节点的插入的区别就在于i<x(头节点)
                                //i<=x(表头节点),因为表头节点不占用下标为0的元素
	{
		p = p->m_link;
	}
	//创建一个新的节点
	q = new(List);//新节点的指针域
	q->element = date;//新节点的数据域

	//插入在p指向的节点后
	if (x > -1)
	{
		q->m_link = p->m_link;
		p->m_link = q;
	}
	
	L.n++;
	return OK;

}

 (3)删除

//删除
int Manager::Delete(int x, singleList& L)
{
	if (L.n==0)
	{
		return NO;
	}

	List* p;
	p = L.head;
	List* q;

	//找到删除的节点的前一个节点
	for (int i = 0; i < x; i++)//若是i<=x,则p为删除的节点
                               //若在头节点中,i<x为删除的节点
	{
		p = p->m_link;
	}

	q=p->m_link;//删除的节点

	if(x>-1)
	{
		p->m_link=q->m_link;
		
	}
	delete q;
	L.n--;
	
	return OK;

}

带表头节点的链表与头节点链表的区别就是i<x(头节点)和i<=x(表头节点),即表头指针移动的次数比头指针移动到目标节点的次数多一次,因为不占用0下标

8.单循环链表

//插入(x为插入的下标,date为插入的元素)
int Manager::Insert(int x, Type date, singleList& L)
{
	List* p;
	p = L.head;
	List* q;

	//插入
	//先找到插入下标的指针域
	for (int i = 0; i <= x; i++)/
	{
		p = p->m_link;
	}
	//创建一个新的节点
	q = new(List);//新节点的指针域
	q->element = date;//新节点的数据域

	//插入在p指向的节点后
	if (x == n)//最后一个元素
    {
        q->m_link=head;
        p->m_link=q;
    else if (x > -1 )//非最后一个元素
	{
		q->m_link = p->m_link;
		p->m_link = q;
	}
	
	L.n++;
	return OK;

}

单循环链表指的就是最后一个元素的指针域不为空,指向第一个元素(即表头节点),形成一个单向循环,所以只需分两种情况:插入的是否为最后一个元素便可以实现

9.双向链表

双向链表是为了解决不仅可以从前往后查询,还可以从后往前查询(查靠后元素便用从后往前,效率比起只能单向高)

(1)插入

class List
{
public:
	//数据域
	Type element;

	//指针域
	List* L_m_link;//左指针
    List* R_m_link;//右指针
};

//插入(x为插入的下标,date为插入的元素)
int Manager::Insert(int x, Type date, singleList& L)
{
	List* p;
	p = L.head;
	List* q;

	//插入
	//先找到插入下标的后一个元素的指针域
	for (int i = 0; i <= x+1; i++)
	{
		p = p->R_m_link;//可以根据左或右指针域查找,右为从前往后,左为从后往前
	}
	//创建一个新的节点
	q = new(List);//新节点的指针域
	q->element = date;//新节点的数据域

	//插入在p指向的节点前
	if (x > -1)
	{
        q->L_m_link=p->L_m_link;//先将后一个的左指针给插入元素,让插入元素的左指针指向前一个元素
        q->R_m_link=p;//再将插入元素的右指针指向后一个元素
        p->L_m_link->R_m_link=q;//然后将前一个元素的右指针指向插入元素
        p->L_m_link=q;//最后将后一个元素的左指针指向插入元素
	}
	
	L.n++;
	return OK;

}

插入的核心为将p指针的指针域利用完再对其覆盖,p指针指向插入元素的后一个元素,所以其左指针肯定指向插入元素,右指针与插入元素无关,所以需要先利用p指针的左指针域,再利用插入元素的前一个元素的右指针域

(2)删除

//删除
int Manager::Delete(int x, singleList& L)
{
	if (L.n==0)
	{
		return NO;
	}

	List* p;
	p = L.head;

	//找到删除的节点
	for (int i = 0; i <= x; i++)
	{
		p = p->R_m_link;//从前往后查找
	}


	if(x>-1)
	{
		p->L_m_link->R_m_link=p->R_m_link;//将删除元素的前一个元素的右指针域指向删除元素的后一个元素
		p->R_m_link->L_m_link=p->L_m_link;//将删除元素的后一个元素的左指针域指向删除元素的前一个元素
	}
	delete q;
	L.n--;
	
	return OK;

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值