“波吉向你发出学习邀请”,数据结构之单双向链表

118 篇文章 62 订阅

目录

嗨,这里是狐狸​

单链表​

单向链表的节点

 单向链表实验

实验内容:

实验说明 :

源代码 

运行截图:

双向链表​

双向链表的创建

双向链表实验

实验内容:

实验说明:

源代码

 总结​


青春的列车上,如果你要提前下车,请别推醒装睡的我

嗨,这里是狐狸

今天是周末,昨日一夜未眠,不知怎的失眠了,没事就爬起来谢谢博客吧,最近中年的焦虑慢慢蔓延上心头,浑身不舒服,或许坐在电脑屏幕前才能得到一丝宁静吧。今天来给大家分享的是数据结构的知识——单链表以及双向链表。

单链表

         单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) +指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。

单向链表的节点

       如下代码所示,双向链表的节点包含两个指向关系和一个数据空间,两个指向分别连接该节点的上一个和下一个节点,数据类型可以是一个结构体类型,也可以是其他类型。

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】了解。想要对程序员的未来发展有兴趣的可以关注微信公众号:【狐狸的编码时光】,希望和大家一起学习进步!

念七可爱卡通情侣头像 可做表情包的卡通情侣头像

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值