数据结构与算法——链表的c++实现

文章提供了C++代码实现单链表的初始化、判断是否为空、销毁、清空、获取长度、插入、删除、查找以及打印所有数据等功能。此外,还涵盖了单链表的头插法和尾插法创建,以及链表的合并。对于双向链表,实现了初始化和在指定位置插入、删除元素的基本操作。
摘要由CSDN通过智能技术生成

单链表的代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>

using namespace std;


#define MAXSIZE 100
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2

// 链表定义
typedef struct Lnode
{
	int data;  // 结点的数据域
	struct Lnode* next; // 结点的指针域
}Lnode, *LinkList;

// 链表初始化
bool InitList(LinkList& L)
{
	L = new Lnode;  // 堆区开辟一个新结点作为链表的头结点(head),同时L作为头指针指向头结点
	L->next = NULL;  // 初始化,就是把头结点的指针域(next)指向空
	return true;
}

// 判断是否为空
int IsEmpty(LinkList& L)
{
	if (L->next == NULL) return TRUE;
	return FALSE;
}

// 销毁单链表
int DestoryList(LinkList& L)
{
	LinkList p;  // 创建一个指针
	while (L != NULL)
	{
		p = L;
		L = L->next;
		delete p;
	}
	return OK;
}

// 清空链表
int ClearList(LinkList& L)
{
	LinkList p;  
	LinkList q;  // 创建两个指针 用于依次释放结点
	p = L->next;  // 将指针 p 指向首元结点处

	while (L != NULL)
	{
		q = p->next;  // 将p指向的那个结点的指针域赋给 q,实际上就是让 q 指向 p 的下一个结点
		delete p;  // 删除 p指向的结点
		p = q;  // 让p跑到q指向的那个结点
	}
	L->next = NULL;  // 头结点指针域为空
	return OK;
}

// 求单链表的表长
int GetLength(LinkList L)  //这里只要统计表长,不改变链表元素,因此不用 &
{
	LinkList p;  // 创建一个指针  或者写成:Lnode *p
	p = L->next;  // 将头结点的 next域值赋给 p,

	int i = 0;
	while (p != NULL)  // 判断 指针p 指向的结点是否为空,不为空继续执行,为空跳出循环
	{
		i++;
		p = p->next;
	}
	return i;
}

// 取链表中第 i 个元素的值
int GetElem(LinkList L, int i, int& e)
{
	LinkList p;
	p = L->next;  // p指向首元结点
	int j = 1;  // 因为 p 已经在首元节点了,所以这里赋值直接使 j=1

	while (p != NULL && j < i)  // 依次向后扫描,直到p指向第i个元素或者p指向空
	{
		p = p->next;
		j++;
	}
	if (!p || j>i)
	{
		cout << "out of range" << endl;
		return ERROR;
	}
	e = p->data;
	return OK;
}

// 查找 - 按值查找,返回地址  —— 地址要用 指针形式返回
Lnode* LocateElem(LinkList L, int e)
{
	LinkList p;
	p = L->next;  // p指向首元结点
	while (p != NULL && p->data != e)
	{
		p = p->next;
	}
	return p;  // 不管找没找到都返回地址,只是没找到时 p 指向空处
}

// 查找 - 按值查找,返回位置序号
int LocateElem_L(LinkList L, int e)
{
	LinkList p;
	p = L->next;  // p指向首元结点
	int j = 1; 

	while (p != NULL && p->data != e)
	{
		p = p->next;
		j++;
	}
	// 这里意思是 指针p 如果找到了肯定不指向空,因此返回j;如果没找到肯定遍历链表一遍了,所以指向空处,所以返回0
	if (p != NULL)  // p!=NULL  等价于 p
	{
		return j;
	}
	else
	{
		return 0;
	}
}

// 在链表 第 i 个位置插入 新结点 e
bool InsertList(LinkList& L, int i, int e)
{
	LinkList p;  //
	p = L;  // 将指针p指向L,也就是指向头指针
	int j = 0;  // 所以这里从 j=0 开始算起

	while (p != NULL && j<i-1)  //寻找第 i-1 个结点,让指针 p 指向第 i-1 个结点位置
	{
		p = p->next;
		++j;
	}
	if (!p || j>i-1)  // i 大于表长 +1 或者小于1,插入位置违法
	{
		return ERROR;
	}
	Lnode *s = new Lnode;  //创建一个新结点   LinkList s = Lnode *s
	s->data = e;

	s->next = p->next;  // 这两步顺序不能颠倒,否则p的next域存放的地址找不到了
	p->next = s;
	return true;
}

// 删除结点
int DeleteLnode(LinkList& L, int i, int& e)
{
	LinkList p;  // 创建指针 p 指向第 i-1 个结点的位置,不能直接指向第 i 个位置,因为后面要删除
	p = L;
	int j = 0;

	while (p->next != NULL && j<i-1)
	{
		p = p->next;  // 寻找第 i-1 个结点,并令 p 指向这个位置的结点
		++j;
	}
	if (!p || j>i-1)
	{
		return ERROR;
	}
	
	LinkList q;  // 创建指针指向被删除的结点,临时保存结点地址,防止后面找不到
	q = p->next;  //把第 i 个结点的地址赋给指针 q保存
	p->next = p->next->next;  // 将第 i+1 个结点地址赋给 第 i-1 个结点的next域
	e = q->data;  // 用e 保存第i个结点的数据域

	// 释放删除结点的空间,另外说明:这里不特地删除指针 q,是因为指针 q 不是在堆区开辟,即不是 new 出来的,所以局部变量执行完毕后,还给栈区了	
	delete q;  
	return OK;
}

// 打印单链表所有数据域的数据
void PrintList(LinkList L)
{
	LinkList p;
	p = L->next;  // p 指向首元结点
	while (p != NULL)
	{
		cout << p->data << "\t";
		p = p->next;
	}
	cout << endl;
}

// 单链表的建立 —— 头插法
void CreateListHead(LinkList& L, int n)
{
	L = new Lnode;  // 创建头结点;在堆区开辟;注意这里new过之后才是结点,否则就是一个指针
	L->next = nullptr; // c++11 写法

	for (int i = n; i >0; --i)
	{
		Lnode* p = new Lnode;  // 创建新结点
		cin >> p->data;
		//解释:第一次把p的next域指针置为NULL,意思是最后一个结点的next域为空;而后来的结点的next域存放为前一个结点的地址
		p->next = L->next;   // 每次换尾巴
		L->next = p;  // 每循环一次就将 L的next域存放的地址 覆盖成 最新创建的结点的地址; 每次换头
	}
}

// 单链表的建立 —— 尾插法
void CreateListTail(LinkList& L, int n) // 通过 & 带回链表
{
	L = new Lnode;  //创建头结点;在堆区开辟;注意这里new过之后才是结点,否则就是一个指针
	L->next = nullptr;

	LinkList r;  // 创建一个指针,作为尾指针
	r = L;  // 尾指针指向头结点
	for (int i = 0; i < n; i++)  // 依次正序插入n次
	{
		Lnode* p = new Lnode;  // 创建新结点		
		cin >> p->data;
		p->next = nullptr;

		r->next = p;  // 插入到表尾
		r = p;  // 每连接上一个新结点,就把尾指针向后移到新结点上,所以称之为尾指针
	}
}

// 单链表的合并 —— 比较数据域的数据大小升序排序
void MergeList(LinkList& La, LinkList& Lb, LinkList& Lc)
{
	LinkList pa, pb, pc;  // 指针
	pa = La->next;    // pa指向首元结点
	pb = Lb->next;  

	pc = Lc = La;  //用La的头结点作为Lc的头结点
	while (pa && pb)  // 等价于 pa->next != nullptr && pb->next != nullptr
	{
		if (pa->data <= pb->data) { pc->next = pa; pc = pa; pa = pa->next; }
		else
		{
			pc->next = pb; pc = pb; pb = pb->next;
		}
	}
	// 插入剩余段,看pa和pb那个剩余,把剩下的都赋给 pc
	pc->next = pa ? pa : pb;  // 三目运算 等价于:
	delete Lb;  // 释放Lb的头结点
}


int main()
{
	// 实验1:尾插法
	LinkList L1;
	InitList(L1); // 链表初始化
	cout << IsEmpty(L1) << endl;  // 判断是否为空
	CreateListTail(L1, 3);  // 通过尾插法
	InsertList(L1, 1, 10);
	InsertList(L1, 3, 30);
	InsertList(L1, 5, 50);
	cout << IsEmpty(L1) << endl;
	cout << GetLength(L1) << endl;
	PrintList(L1);
	DestoryList(L1);

	// 实验2:头插法
	LinkList L2;
	InitList(L2); // 链表初始化
	cout << IsEmpty(L2) << endl;  // 判断是否为空
	cout << "请依次输入数据:" << endl;
	CreateListHead(L2, 3);  // 通过头插法
	InsertList(L2, 2, 10);
	InsertList(L2, 4, 30);
	InsertList(L2, 6, 50);
	//cout << IsEmpty(L2) << endl;
	cout << GetLength(L2) << endl;
	PrintList(L2);
	DestoryList(L2);

	// 实验3:单链表的合并
	LinkList La, Lb, Lc;
	InitList(La);
	InitList(Lb);
	
	InsertList(La, 1, 10);
	InsertList(La, 2, 30);
	InsertList(La, 3, 50);
	InsertList(La, 4, 70);

	InsertList(Lb, 1, 5);
	InsertList(Lb, 2, 21);
	InsertList(Lb, 3, 33);

	MergeList(La, Lb, Lc);
	PrintList(Lc);


	return 0;
}

双向链表:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>

using namespace std;


#define MAXSIZE 100
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2



// 双向链表定义
typedef struct DuLnode
{
	int data;  // 结点的数据域
	struct DuLnode *prior, *next; // 结点的指针域 - 
}DuLnode, *DuLinkList;

// 双向链表初始化
bool InitList(DuLinkList& L)
{
	L = new DuLnode;  // 堆区开辟一个新结点作为链表的头结点(head),同时L作为头指针指向头结点
	L->prior = nullptr;  //(prior)指向空
	L->next = nullptr;  // 初始化,就是把头结点的指针域(next)指向空
	return true;
}

// 在双向链表的第 i 个位置插入元素
int ListInsert_DuL(DuLinkList& L, int i, int e)
{
	DuLnode* p = new DuLnode;
	p = L->next;
	int j = 1;

	while (p && j < i)
	{
		++j;
		p = p->next;
	}
	if (j < i || j < 1) //如果i在链表范围内,上面的while循环的终止条件就是j<i
	{
		cerr << "out of range" << endl;
		return false;
	}
	// 堆区开辟想要插入的结点
	DuLnode* s = new DuLnode;
	s->data = e;
	// 重新链接
	s->prior = p->prior;   //第一步:p的prior域存放的是原来的前结点地址,赋给s的prior域,所以s的prior就能访问前节点
	p->prior->next = s;   // 
	s->next = p;
	p->prior = s;

	return OK;
}

// 双向链表删除某个元素
int DeleteListElem_Dul(DuLinkList& L, int i, int& e)
{
	DuLnode* p = new DuLnode;
	p = L->next;
	int j = 1;

	while (p && j < i)
	{
		++j;
		p = p->next;
	}
	if (j < i || j < 1) //如果i在链表范围内,上面的while循环的终止条件就是j<i
	{
		cerr << "out of range" << endl;
		return false;
	}
	e = p->data;
	// 这里不考虑最后一个节点情况,如果考虑只要在后面两条加上 if 判断条件
	p->prior->next = p->next;  // 把p的后一个节点的地址赋给p的前一个节点next域保存
	p->next->prior = p->prior; // 把p的前一个节点的地址赋给p的后一个节点prior域保存

	delete p;
	return OK;
}

int main()
{
	// 这里就不再写了,思路都是跟单链表一样
	// 如果有人看到,可以继续写 前插,后插,打印输出就能使用了

	return 0;
}

视频的三道案例有机会再写~~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值