目录
序
青春的列车上,如果你要提前下车,请别推醒装睡的我
嗨,这里是狐狸
今天是周末,昨日一夜未眠,不知怎的失眠了,没事就爬起来谢谢博客吧,最近中年的焦虑慢慢蔓延上心头,浑身不舒服,或许坐在电脑屏幕前才能得到一丝宁静吧。今天来给大家分享的是数据结构的知识——单链表以及双向链表。
单链表
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) +指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。
单向链表的节点
如下代码所示,双向链表的节点包含两个指向关系和一个数据空间,两个指向分别连接该节点的上一个和下一个节点,数据类型可以是一个结构体类型,也可以是其他类型。
typedef struct node
{
int data;
struct node *pre;
struct node *next;
}Node;
单向链表实验
实验内容:
1.随机产生或键盘输入一组元素(不少于10个元素),建立一个带头结点的单链表。
2.把单链表中的元素逆置(不允许申请新的结点空间)。
3.删除单链表中所有的偶数元素结点。
4.编写在非递减有序链表中插入一个元素使链表元素仍有序的函数,利用该函数建立一个
非递减有序单链表。
5.利用算法4建立两个非递减有序单链表,然后合并成一个非递增链表。
6.把算法1建立的链表分解成两个链表,其中一个全部为奇数,另一个全部为偶数(尽量
利用已有存储空间)。
7.在主函数中设计一个简单菜单,调用上述算法。
实验说明 :
1.结点类型定义
typedef int ElemType; // 元素类型
typedef struct LNode
{
ElemType data ;
struct LNode * next ;
} LNode, *pLinkList ;
2.为了简单,采用带头结点的单链表。
源代码
#include <iostream>
#include <time.h>
#include <iomanip>
#include <stdlib.h>
using namespace std;
typedef int ElemType;
typedef struct LNode
{
ElemType data;
struct LNode* next;
}LNode, *pLinkList;
pLinkList LinkList_Init() //单链表创建
{
int n;
LNode *phead, *temp, *p;
phead = new LNode;
if (phead == NULL)
{
cout << "链表创建失败!" << endl;
exit(0);
}
cout << "输入需要随机产生的元素的个数(不少于10个):";
cin >> n;
temp = phead;
srand(unsigned(time(NULL)));
for (int i = 0; i < n; i++)
{
p = new LNode;
p->data = rand() % 100;
temp->next = p;
temp = p;
}
temp->next = NULL;
return phead;
}
void LinkList_Display(pLinkList phead) //输出链表
{
phead = phead->next;
while (phead)
{
cout << setw(4) << phead->data;
phead = phead->next;
}
cout << endl;
}
void LinkList_Free(pLinkList phead) //链表释放
{
LNode *p;
while (phead)
{
p = phead;
phead = phead->next;
delete p;
}
}
void LinkList_Convert(pLinkList phead) //单链表中的元素逆置
{
LNode *p1, *p2, *p3; //p3为第一个结点,p2为第二个结点,p1为第三个结点
if (phead->next == NULL)
{
cout << "链表为空!" << endl;
return;
}
p3 = phead->next;
p2 = p3->next;
if (p2 == NULL)
{
return;
}
p1 = p2->next;
p3->next = NULL;
while (p1)
{
p2->next = p3;
p3 = p2;
p2 = p1;
p1 = p1->next;
}
p2->next = p3;
phead->next = p2;
}
void LinkLIst_Del_oushu(pLinkList phead) //删除单链表中所有的偶数元素结点
{
LNode *p1, *p2;
p2 = phead;
phead = phead->next;
while (phead)
{
if (phead->data % 2 == 0)
{
p1 = phead;
phead = phead->next;
delete p1;
p2->next = phead;
continue;
}
p2 = phead;
phead = phead->next;
}
}
void LinkList_Sort(pLinkList phead) //排序
{
ElemType *num, temp;
LNode *p;
int count = 0, i = 0;
if (phead->next == NULL)
{
cout << "链表为空!" << endl;
return;
}
p = phead->next;
while (p)
{
count++;
p = p->next;
}
num = new ElemType[count];
p = phead->next;
while (p)
{
num[i++] = p->data;
p = p->next;
}
for (int j = 0; j < count - 1; j++)
for (int k = 0; k < count - j - 1; k++)
if (num[k] > num[k + 1])
{
temp = num[k];
num[k] = num[k + 1];
num[k + 1] = temp;
}
p = phead->next;
i = 0;
while (p)
{
p->data = num[i++];
p = p->next;
}
delete[] num;
}
void LinkList_Insert(pLinkList phead, ElemType elem) //插入一个元素
{
LNode *p1, *p2, *tmp = NULL;
p1 = phead->next;
p2 = phead;
while (p1)
{
if (elem < p1->data)
break;
p2 = p1;
p1 = p1->next;
}
tmp = new LNode;
tmp->data = elem;
p2->next = tmp;
tmp->next = p1;
}
void LinkList_Divide(pLinkList L1, pLinkList L2) //链表分解成奇数和偶数两个链表
{
LNode *p1, *p2;
p2 = L1;
p1 = L1->next;
while (p1)
{
if ((p1->data) % 2 == 0)
{
L2->next = p1;
L2 = p1;
p1 = p1->next;
p2->next = p1;
}
else
{
p2 = p1; p1 = p1->next;
}
}
L2->next = NULL;
}
void LinkList_Cat(pLinkList L1, pLinkList L2) //链表合并,
{
pLinkList p1, p2;
p2 = L1;
p1 = L1->next;
while (p1)
{
p2 = p1; p1 = p1->next;
}
p2->next = L2->next;
LinkList_Sort(L1);
LinkList_Convert(L1);
}
int main()
{
LNode *L = NULL, *L1 = NULL, *L2 = NULL;
int num;
cout << "1:随机产生或键盘输入一组元素(不少于10个元素),建立一个带头结点的单链表" << endl;
cout << "2:单链表中的元素逆置" << endl;
cout << "3:单链表排序输出" << endl;
cout << "4:删除单链表中所有的偶数元素结点" << endl;
cout << "5:插入一个元素,用该函数建立一个非递减有序单链表(插入)" << endl;
cout << "6:建立两个非递减有序单链表,然后合并成一个非递增链表(合并)" << endl;
cout << "7:链表分解成两个链表,其中一个全部为奇数,另一个全部为偶数" << endl;
cout << "8:退出" << endl;
cout << endl;
while (true)
{
cout << "请输入一个数字选项:";
cin >> num;
switch (num)
{
case 1:
{ L = LinkList_Init();
cout << "L链表为:";
LinkList_Display(L);
cout << endl;
}break;
case 2:
{ LinkList_Convert(L);
cout << "链表为:";
LinkList_Display(L);
cout << endl;
}break;
case 3:
{ LinkList_Sort(L);
cout << "链表为:";
LinkList_Display(L);
cout << endl;
}break;
case 4:
{ LinkLIst_Del_oushu(L);
cout << "链表为:";
LinkList_Display(L);
cout << endl;
}break;
case 5:
{ ElemType elem;
cout << "输入要插入的元素:";
cin >> elem;
LinkList_Sort(L);
LinkList_Insert(L, elem);
cout << "链表为:";
LinkList_Display(L);
cout << endl;
}break;
case 6:
{
L1 = LinkList_Init();
cout << "L1链表为:";
LinkList_Display(L1);
L2 = LinkList_Init();
cout << "L2链表为:";
LinkList_Display(L2);
LinkList_Cat(L1, L2);
cout << "合并的链表为:";
LinkList_Display(L1);
cout << endl;
}break;
case 7:
{
L2 = new LNode;
LinkList_Divide(L1, L2);
cout << "L1链表为:";
LinkList_Display(L1);
cout << "L2链表为:";
LinkList_Display(L2);
LinkList_Free(L2);
cout << endl;
}break;
case 8:
{
LinkList_Free(L);
delete L1;
cout << endl;
cout << "链表已释放并删除"<<endl;
cout << endl;
}break;
default:
cout << "输入错误!请重新输入!" << endl;
cout << endl;
}
}
return 0;
}
运行截图:
双向链表
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。
双向链表的创建
头插法和尾插法只看代码比较难以与理解,建议画出插入节点时的链表变化图则会非常容易理解,另外,个人认为头插法较优,因为其插入逻辑一步到位,不像尾插法还需在后面完成双向链表的环状连接。
1.头插法
Node * creatList()
{
Node * head = (Node*)malloc(sizeof(Node));
Node * cur = NULL;
head->next = head;
head->pre = head;
int data;
scanf("%d",&data);
while(data)
{
cur = (Node*)malloc(sizeof(Node));
cur->data = data;
cur->next = head->next;
cur->pre = head;
head->next = cur;
cur->next->pre = cur;
scanf("%d",&data);
}
return head;
}
2.尾插法
Node * creatList()
{
Node * head = (Node*)malloc(sizeof(Node));
Node * phead = head;
Node * cur = NULL;
phead->next = NULL;
phead->pre = NULL;
int data;
scanf("%d",&data);
while(data)
{
cur = (Node*)malloc(sizeof(Node));
cur->data = data;
phead->next = cur;
cur->pre = phead;
phead = cur;
scanf("%d",&data);
}
//完成回环
cur->next = head;
head->pre = cur;
return head;
}
双向链表实验
实验内容:
1.利用尾插法建立一个双向链表。
2.遍历双向链表。
3.实现双向链表中删除一个指定元素。
4.在非递减有序双向链表中实现插入元素e仍有序的算法。
5.判断双向链表中元素是否对称,若对称返回 1,否则返回 0。
6.设元素为正整型,实现算法把所有奇数排列在偶数之前。
7.在主函数中设计一个简单菜单,调用上述算法。
实验说明:
双向链表的类型定义
typedef int ElemType; // 元素类型
typedef struct DuLNode
{
ElemType data;
DuLNode *prior, *next;
} DuLNode, *pDuLinkList;
源代码
#include <iostream>
#include <time.h>
#include <stdlib.h>
#include <iomanip>
using namespace std;
typedef int ElemType;
typedef struct DuLNode
{
ElemType data;
struct DuLNode *prior, *next;
} DuLNode, *pDuLinkList;
pDuLinkList DuLinkLinst_Init() //建立双向链表
{
int n;
DuLNode *phead, *tmp, *p;
srand(unsigned(time(NULL)));
cout << "输入建立链表的元素个数:";
cin >> n;
phead = new DuLNode;
phead->next = phead;
phead->prior = phead;
p = phead;
for (int i = 0; i < n; i++)
{
tmp = new DuLNode;
tmp->data = rand() % 100;
tmp->prior = p;
p->next = tmp;
p = tmp;
}
p->next = phead;
phead->prior = p;
return phead;
}
void DuLinkList_Display(pDuLinkList phead) //输出双向链表
{
DuLNode *p;
p = phead->next;
while (p != phead)
{
cout << setw(4) << p->data;
p = p->next;
}
cout << endl;
}
void DuLinkList_Free(pDuLinkList phead) //释放双链表
{
DuLNode *p, *t;
p = phead->next;
while (p != phead)
{
t = p;
p = p->next;
delete t;
}
delete p;
}
void DuLinkList_Del(pDuLinkList phead, ElemType x) //删除指定元素
{
DuLNode *p1, *p2;
p1 = phead->next;
p2 = phead;
while (p1)
{
if (x == p1->data)
{
p2->next = p1->next;
p1->next->prior = p2;
delete p1;
return;
}
p2 = p1;
p1 = p1->next;
}
cout << "指定元素未找到!" << endl;
}
void DuLinkList_Sort(pDuLinkList phead) //排序
{
DuLNode *p;
int count = 0, i = 0;
ElemType *num, tmp;
p = phead->next;
if (p == phead)
{
cout << "双向链表为空!" << endl;
return;
}
while (p != phead)
{
count++;
p = p->next;
}
num = new ElemType[count];
p = phead->next;
while (p != phead)
{
num[i++] = p->data;
p = p->next;
}
for (int k = 0; k < count - 1; k++)
for (int j = 0; j < count - k - 1; j++)
if (num[j] > num[j + 1])
{
tmp = num[j];
num[j] = num[j + 1];
num[j + 1] = tmp;
}
p = phead->next;
i = 0;
while (p != phead)
{
p->data = num[i++];
p = p->next;
}
delete[] num;
}
void DuLinkList_Insert(pDuLinkList phead, ElemType elem) //插入一个元素
{
DuLNode *p, *tmp;
p = phead->next;
while (p != phead)
{
if (elem <= p->data)
break;
p = p->next;
}
tmp = new DuLNode;
tmp->data = elem;
tmp->next = p;
tmp->prior = p->prior;
p->prior->next = tmp;
p->prior = tmp;
}
int DuLinkList_symmetry(pDuLinkList phead) //链表中元素是否对称
{
DuLNode *p1, *p2;
p1 = phead->prior;
p2 = phead->next;
while (p1 != p2)
{
if (p1->data != p2->data)
{
return 0;
}
p1 = p1->prior;
p2 = p2->next;
}
return 1;
}
void DuLinkList_jiousort(pDuLinkList phead) //奇数在偶数前面
{
DuLNode *p1, *p2;
p1 = phead->next;
p2 = p1->next;
if (p1 == phead)
{
cout << "双向链表为空!" << endl;
return;
}
if (p2 == phead)
{
return;
}
while (p1 != phead)
{
if ((p1->data) % 2 != 0)
{
p2 = p1;
p1 = p1->next;
p1->prior = p2->prior;
p2->prior->next = p1;
p2->next = phead->next;
phead->next->prior = p2;
phead->next = p2;
p2->prior = phead;
}
else
p1 = p1->next;
}
}
int main()
{
DuLNode *L = NULL;
ElemType t;
int num;
cout << "1:利用尾插法建立一个双向链表" << endl;
cout << "2:遍历双向链表" << endl;
cout << "3:双向链表中删除一个指定元素" << endl;
cout << "4:在非递减有序双向链表中实现插入元素e仍有序的算法。" << endl;
cout << "5:判断双向链表中元素是否对称,若对称返回 1,否则返回 0" << endl;
cout << "6:设元素为正整型,实现算法把所有奇数排列在偶数之前" << endl;
cout << "7:退出" << endl;
while (true)
{
cout << "请输入一个数字选项:";
cin >> num;
switch (num)
{
case 1:
{
L = DuLinkLinst_Init();
cout << "L双向链表为:";
DuLinkList_Display(L);
cout << endl;
}break;
case 2:
{
cout << "双向链表为:";
DuLinkList_Display(L);
cout << endl;
}break;
case 3:
{
cout << "输入需要删除的元素:";
cin >> t;
DuLinkList_Del(L, t);
cout << "双向链表为:";
DuLinkList_Display(L);
cout << endl;
}break;
case 4:
{
cout << "输入要插入的元素:";
cin >> t;
DuLinkList_Sort(L);
DuLinkList_Insert(L, t);
cout << "双向链表为:";
DuLinkList_Display(L);
cout << endl;
}break;
case 5:
{
if(DuLinkList_symmetry(L)==1)
cout << "双向链表中元素对称" << endl;
else
cout << "双向链表中元素不对称" << endl;
cout << endl;
}break;
case 6:
{
DuLinkList_jiousort(L);
cout << "双向链表为:";
DuLinkList_Display(L);
cout << endl;
}break;
case 7:
{
DuLinkList_Free(L);
cout << endl;
return 0;
}break;
default:
cout << "输入错误!请重新输入!" << endl;
cout << endl;
}
}
return 0;
}
运行截图:
总结
如果把编程语言比作一个句子,那数据结构就好比是一篇文章。写好句子要意思清晰明确,写文章的话,更看重的是其传达的思想。马克吐温有句话是这样说的,当你手拿着锤子的时候,看见很多东西都是钉子。这句话原来的意思我不大想深究,毕竟这不是技术问题。而在读过了著名黑客雷蒙德的一篇经典文章《如何成 为一名黑客》,我对这个说法有一个新的感悟。他在文中写道:“做一名黑客会有很多乐趣,但却 是要费很多气力方能得到的乐趣。 这些努力需要动力。成功的运动员从锻炼身体、超越自我极限的愉悦中得到动力。 同样,做黑客,你得能从解决问题,磨练技术及锻炼智力中得到基本的乐趣。”锤子是工具,钉子是问题,当工具被用来解决你的问题的时候,享受那种满足的感觉, 是很难用言语来形容的。学习 C 语言不是光为了学习 C 语言,你是要用它来解决问题的。很遗憾在现实中,很多同学在大一大二学完了C 语言这门课程,甚至也考了一个不错的分数之后, 就把这门工具丢到一旁了,再也没理了。他们没有享受到这种乐趣,这是很可惜的。你是一个手拿锤子的人吗?握紧!
好叭。今天就到这里了,我也该去睡觉了,后续我还会发布更多的项目源或者学习资料,希望大家可以持续关注,有什么问题可以回帖留言。想要提前掌握的可以加群领取C/C++学习资料以及其他项目的源码的可以加群【1083227756】了解。想要对程序员的未来发展有兴趣的可以关注微信公众号:【狐狸的编码时光】,希望和大家一起学习进步!