【数据结构】链表相关——单链表/循环链表/双向链表/学生管理系统单链表实现/约瑟夫环/多项式加法

链表

目录

单链表

补充指针:举个例子,int num = 5;有个整型的变量num,存储整型数据5。int* p_num = #有个指针类型变量p_num,存储的是num变量的地址,所以我们可以说指针p_num指向变量num

  • 顺序表—>静态存储分配—>事先确定容量

  • 链表—>动态存储分配—>运行时分配空间

  • 单链表:线性表的链接存储结构

  • 存储思想:用一组任意(不连续、零散分布)的存储单元存放线性表的元素

  • 单链表的存储特点:

    • 逻辑次序和物理次序不一定相同

    • 元素之间的逻辑关系用指针表示

      在这里插入图片描述

  • 单链表是由若干个结点构成的;单链表的结点只有一个指针域

    typedef struct node{
      DataType data; // 数据域
      struct node *next; // 指针域
    }Node,*Link;
    
    Node st; 等价于 struct node st;
    Link p; 等价于 struct node *p; // 一个指向结构体的指针(指针的本质实际上是一种特殊的数据类型,指针类型的变量存的就是地址)
    p=&st;
    
    
    // 申请一个存储单元
    // 通过malloc()函数声明一块空间 用p指向这块空间
    p = (Link)malloc(sizeof(Node));  等价于  p = (struct node*)malloc(sizeof(Node));
    
    
    // 引用数据元素
    (*p).data  或者  p->data
    // 引用指针域
    p->next
    
  • 存储结构:重点在数据元素之间的逻辑关系的表示。所以,将实际存储地址抽象(单链表其实是零散在内存中的)

    在这里插入图片描述

    头指针:指向第一个结点的地址

    尾标志:终端结点的指针域为空

  • 上图中的空表和非空表的表示不一致,如何将其统一?

    在这里插入图片描述

    头结点:在单链表的第一个元素结点之前附设一个类型相同的结点,以便空表和非空表统一处理

  • 单链表的实现

    • 遍历操作

      void displayNode(Link head){
        p = head->next;
        while(p!=NULL){
          printf("%d",p->data);
          p=p->next;
        }
      }
      
    • 求单链表的元素个数

      int length(Link head){
        p = head->next;
        int count = 0;
        while(p!=NULL){
          count++;
          p=p->next;
        }
        return count;
      }
      
    • 查找操作

      bool queryNode(Link head,DataType x){
        p = head->next;
        while(p!=NULL){
          if(p->data==x){
            printf("%d",p->data);
            return true;
          }
          p=p->next;
        }
        return false;
      }
      
    • 插入操作

      bool insertNode(Link head,int i,DataType x){ // 在i前面插入一个结点
        p=head; // 注意这里和上面的代码有点不一样 因为插入的地方可能是在第一个元素之前
        int count = 0;
        while(p!=NULL && count < i - 1){
          count++;
          p=p->next;
        }
        if(p==NULL) return false; // 判断i-1处是否为NULL || p现在是否为空(还没到i-1)
        node = (Link)malloc(sizeof(Node)); // node存的是地址
        node->data=x;
        node->next=p->next;
        p->next=node;
        return true;
      }
      
    • 创建单链表

      // 头插法:将待插入结点插在头结点的后面,得到的链表的顺序和数组的顺序是相反的(数组的尾部在前头部在后)
      Link newList(DataType a[],int n){
        // 初始化头结点
        head = (Link)malloc(sizeof(Node));
        head->next=NULL;
        // 插入元素
        for(int i = 0;i < n;i++){
          node = (Link)malloc(sizeof(Node));
          node->data=a[i];
          node->next=head->next;
          head->next=node;
        }
        return head; // 只要知道开始位置就可以得到整个链表
      }
      
      // 尾插法:将待插入结点插在终端结点的后面,得到的链表的顺序和数组的顺序是一样的
      Link newList(DataType a[],int n){
        // 初始化头结点和尾结点
        head = (Link)malloc(sizeof(Node));
        head->next=NULL;
        rear=head;
        // 插入元素
        for(int i = 0;i < n;i++){
          node = (Link)malloc(sizeof(Node));
          node->data=a[i];
          rear->next=node;
          rear=node; // 一直保持rear指向最后一个结点
        }
        rear->next=NULL; // 一定要写!
        return head;
      }
      
    • 删除结点

      bool deleteNode(Link head,DataType x){
        // 判断是否为空表
        if(head == NULL || head->next == NULL){
          return false;
        }
        // 删除结点是把要删除的结点的后一个结点赋给前一个结点的next指针
        // 所以需要一直知道要删除的结点的前一个结点是什么(因为单链表只能获取后面的结点 不能获取前面的结点)
        // 初始化时 就保证p和q一前一后的关系
        p=head->next;
        q=head;
        // 在查找的过程中,如果发现p所指向的结点data值不是要找的x,则p、q同时后移;一旦找到,则执行删除操作
        while(p!=NULL){
          if(p->data==x){
            q->next=p->next;
            free(p);
            return true;
          }
          q=p;
          p=p->next;
        }
        return false;
      }
      
    • 释放链表(将单链表中所有结点的存储空间释放)

      void clearLink(Link head) {
          Link q;
          while (head != NULL) {
              // display(head); // 如果display()方法没有针对空链表进行判断的话,这个函数的调用不要在head=head->next后面。因为head=head->next可能使head为NULL,如果display()函数没有针对空链表判断的话,这里会报错
              q = head;
              head = head->next;
              free(q);
          }
      }
      

循环链表

  • 在单链表中无法找到指定结点的前一个结点,只能找到后一个结点

  • 循环链表:将单链表的首尾相接,将终端终点的指针域由空指针改为指向头结点,构成单循环链表

    在这里插入图片描述

  • 循环链表的特点:

    • 循环链表中没有明显的尾端,要注意防止死循环

      循环条件:
      p!=NULL变成了p!=head
      p->next!=NULL变成了p->next!=head
      
      

双向链表

在这里插入图片描述

  • 双向链表:在单链表的每个结点中再设置一个指向其前驱结点的指针域

  • 结点结构:

    • data:数据域,存储数据元素

    • prior:指针域,存储该结点的前驱结点地址

    • next:指针域,存储该结点的后继结点地址

单链表:学生管理系统

  • 这种系统只能在devc++上跑,不能在clion上跑(clion会把所有的scanf输入全部输入了之后才输出,用cin倒是没问题

    在这里插入图片描述

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    int choose;
    int id;
    char name[10];
    
    typedef struct student {
        int id;
        char name[10];
    } Student;
    
    typedef struct node {
        struct student data;
        struct node *next;
    } Node, *Link;
    
    void showMenu() {
        printf("------------欢迎来到学生管理系统------------\n");
        printf("----------------1.新增学生----------------\n");
        printf("----------------2.查询学生----------------\n");
        printf("----------------3.删除学生----------------\n");
        printf("----------------4.修改学生----------------\n");
        printf("--------------5.打印全部学生---------------\n");
        printf("----------------6.清空学生----------------\n");
        printf("请选择(1-6):");
    }
    
    Student createStudent() {
        Student s;
        printf("请输入学号:");
        scanf("%d", &s.id);
        printf("请输入姓名:");
        scanf("%s", s.name);
        return s;
    }
    
    int inputId() {
        printf("请输入要查询的学生的学号:");
        scanf("%d", &id);
        return id;
    }
    
    int updateId() {
        printf("请输入要修改的学生的学号:");
        scanf("%d", &id);
        return id;
    }
    
    int deleteId() {
        printf("请输入要删除的学生的学号:");
        scanf("%d", &id);
        return id;
    }
    
    char *inputName() {
        memset(name, 0, sizeof(name));
        printf("请输入要修改的学生的姓名:");
        scanf("%s", &name);
        return name;
    }
    
    // 插入学生 并保证学号递增
    bool insertStudent(Link head, Student st) {
        Link node;
        Link p = head->next;
        Link q = head;
        while (p != nullptr) {
            if (p->data.id > st.id) {
                node = (Link) malloc(sizeof(Node));
                node->data.id = st.id;
                for (int i = 0; i < strlen(st.name); ++i) {
                    node->data.name[i] = st.name[i];
                }
                node->next = p;
                q->next = node;
                return true;
            }
            q = p;
            p = p->next;
        }
        // 到了末尾
        node = (Link) malloc(sizeof(Node));
        node->data.id = st.id;
        for (int i = 0; i < strlen(st.name); ++i) {
            node->data.name[i] = st.name[i];
        }
        q->next = node; // p这个时候是null q是最后一个元素
        node->next = nullptr;
        return true;
    }
    
    void selectStudent(int s_id, Link head) {
        Student s;
        Link p = head->next;
        while (p != nullptr) {
            if (p->data.id == s_id) {
                printf(("学生的学号为:%d,姓名为:%s\n"), p->data.id, p->data.name);
                return;
            }
            p = p->next;
        }
        printf("未找到该学生!");
    }
    
    bool deleteStudent(int s_id, Link head) {
        Link p = head->next;
        Link q = head;
        while (p != nullptr) {
            if (p->data.id == s_id) {
                q->next = p->next;
                free(p);
                return true;
            }
            p = p->next;
        }
        return false;
    }
    
    // 修改学生 不允许修改学号
    bool updateStudent(char s_name[10], int s_id, Link head) {
        Link p = head->next;
        while (p != nullptr) {
            if (p->data.id == s_id) {
                for (int i = 0; i < strlen(s_name); ++i) {
                    p->data.name[i] = s_name[i];
                }
                return true;
            }
            p = p->next;
        }
        return false;
    }
    
    void printAllStudent(Link head) {
        Link p = head->next;
        while (p != nullptr) {
            printf(("学生的学号为:%d,姓名为:%s\n"), p->data.id, p->data.name);
            p = p->next;
        }
    }
    
    bool clearAllStudent(Link head) {
        Link p;
        while(head != nullptr){
            p = head;
            head = head->next;
            free(p);
        }
        return true;
    }
    
    int main() {
        // 创建头结点
        Link head = (Link) malloc(sizeof(Node));
        head->next = nullptr;
    
        while (true) {
            showMenu();
            scanf("%d", &choose);
            switch (choose) {
                case 1:
                    insertStudent(head, createStudent());
                    break;
                case 2:
                    selectStudent(inputId(), head);
                    break;
                case 3:
                    deleteStudent(deleteId(), head);
                    break;
                case 4:
                    updateStudent(inputName(), updateId(), head);
                    break;
                case 5:
                    printAllStudent(head);
                    break;
                case 6:
                    clearAllStudent(head);
                    return 0;
                default:
                    printf("请输入正确的数字!");
                    break;
            }
            system("pause");
            system("cls");
        }
    }
    

约瑟夫环

介绍

  • 约瑟夫问题:

    • 有n 只猴子,按顺时针方向围成一圈选大王(编号从1到n ) ,从第1号开始报数,一直数到m,数到m的猴子退出圈外,剩下的猴子再接着从1开始报数。就这样,直到圈内只剩下一只猴子时,这个猴子就是猴王,编程求输入n, m后,输出最后猴王的编号。

循环链表实现

#include<iostream>

using namespace std;

typedef struct node {
    int data;
    struct node *next;
} *Link, Node;

int main() {
    int n, m; // n是猴子个数 m是报数
    Link head = (Link) malloc(sizeof(Node)); // sizeof()的参数应该是整个结构体 malloc()的结果是一个地址 可以强转成Link类型
    head->next = nullptr;
    while (true) {
        cin >> n >> m;
        if (n == 0 || m == 0) { // 当n或m为0时 则退出
            free(head); // 释放head
            break;
        }
        // 尾插法构建链表
        Link tail = head;
        for (int i = 1; i <= n; i++) {
            Link q = (Link) malloc(sizeof(Node));
            q->data = i;
            tail->next = q;
            // 形成循环链表
            q->next = head->next;
            tail = q;
        }
        // 维持p、q一前一后的关系 方便删除
        Link q = tail;
        Link p = head->next;
        int i = 1; // 计数
        while (p != q) { // 当p和q不重叠的时候就进行循环
            if (i == m) { // 报数等于m时 删除节点
                q->next = p->next;
                free(p);
                // 将p移动到下一个有效的节点上
                p = q->next;
                // 重新报数
                i = 1;
            } else {
                i++;
                q = p;
                p = p->next;
            }
        }
        cout << p->data << endl;
        free(p); // 清除该节点
        head->next = nullptr; // 头结点的next赋为空
    }
    return 0;
}、

数组标志位实现

#include <iostream>
using namespace std;

int main(){
    int n,m; // n表示猴子个数 m表示报数
    while(true){
        cin >> n >> m;
        if (n == 0 || m == 0){
            return 0;
        }
        int arr[310] = {0};
        // 赋初值 数组里存的是猴子的编号(从1开始
        for (int i = 0; i < n; ++i) {
            arr[i] = i + 1;
        }
        int num = n; // 防止修改n
        int i = 0; // 用于遍历下标
        int count = 1; // 用于计报数
        while(num > 1){
            if (arr[i] != 0){ // 该数组对应的位数不为0 报数
                if (count == m){
                    num--;
                    arr[i] = 0; // 为0表示该处的猴子已经淘汰了
                    i = (i + 1) % n;
                    count = 1;
                }else{
                    i = (i + 1) % n;
                    count++;
                }
            }else{ // 该数组对应的位数为0 就取下一个
                i = (i + 1) % n;
            }
        }
        for (int j = 0; j < n; ++j) {
            if (arr[j] != 0){
                cout << arr[j] << endl;
            }
        }
    }
}、

数组链接方式实现

用数组模拟循环链表

在这里插入图片描述

#include <iostream>
using namespace std;

int main(){
    int n,m;
    while(true){
        cin >> n >> m;
        if (n == 0 || m == 0){
            return 0;
        }
        int arr[310] = {0}; // 该数组中存的内容是该位置上的元素指向的下一个元素的下标
        for (int i = 0; i < n - 1; ++i) {
            arr[i] = i + 1; // 存的是下一个元素的下标
        }
        arr[n - 1] = 0;
        // 和链表一样 需要维护两个指针 一前一后
        int pos = 0;
        int prior = n - 1;
        int num = n; // 用于计算还剩几个元素没被淘汰
        int count = 1; // 用于计数
        while (num > 1){
            if (count != m){ // 没有到m的时候 则移动指针
                prior = pos;
                pos = arr[pos]; // 时刻记住arr存的是指向的下一个元素的下标
                count++;
            }else{
                num--;
                count = 1;
                arr[prior] = arr[pos]; // 更改链接关系
                arr[pos] = -1; // 淘汰的猴子置为-1
                pos = arr[prior]; // pos指向下一个有效的位置
            }
        }
        // 下面两种输出方法都可以
        // 此时的pos(prior和pos是一样的)就存的是最后一个猴子的下标 因为编号从1开始 所以记得+1
        cout << pos + 1 << endl;
//        for (int i = 0; i < n; ++i) {
//            if (arr[i] != -1){
//                cout << i + 1 << endl;
//                break;
//            }
//        }
    }
}

数学方法

#include <bits/stdc++.h>
using namespace std;
int main(void)
{
    int n,m,i,s=0;
    scanf("%d%d",&n,&m);
    for (i=2; i<=n; i++)
        s=(s+m)%i;
        cout<<s+1<<endl;
    return 0 ;
}

多项式加法

输出多项式

#include <iostream>

using namespace std;

int main() {
    int n;
    int a;
    while (cin >> n) {
        for (int i = n; i >= 0; --i) { // n就是x的次方
            cin >> a;
            if (a == 0){ // 当a等于0时 直接跳过
                continue;
            }

            // 首先处理符号
            if (i == n){ // 最高位  题目明确说了最高位的系数不为0
                if (a < 0){
                    cout << "-";
                }
            }else{
                if (a < 0){
                    cout << "-";
                }else{
                    cout << "+";
                }
            }

            // 然后处理数值
            if (i == 0){ // 最后一位 即x=0时 -1/1都是要输出的
                cout << abs(a);
            }else{
                if (a == 1 || a == -1); // 当a为-1/1时 不需要输出值
                else{
                    cout << abs(a);
                }
            }

            // 最后处理次数
            if (i != 0){ // i等于0时 不用考虑次数的输出
                cout << "x";
                if (i != 1){
                    cout << "^" << i;
                }
            }
        }
        cout<<endl;
    }
    return 0;
}

多项式加法—数组实现

#include <iostream>

using namespace std;

void print(int a[], int n) {
    for (int i = n; i >= 0; --i) {
        if (a[i] == 0) continue; // 等于0则跳过

        // 处理符号
        if (i == n) { // 最高位
            if (a[i] < 0) {
                cout << "-";
            }
        } else { // 其他位
            if (a[i] < 0) {
                cout << "-";
            } else if (a[i] > 0) {
                cout << "+";
            }
        }

        // 处理系数
        if (i == 0) {
            cout << abs(a[i]);
            continue;
        }
        if (abs(a[i]) != 1) { // 系数为1的绝对值的时候 不输出
            cout << abs(a[i]);
        }

        // 处理指数
        cout << "x";
        if (i != 1) { // 当指数不为1时 则输出
            cout << "^" << i;
        }
    }
    cout << endl;
}

int main() {
    int a[101] = {0}; // 存第一个多项式的系数,下标则为指数
//    int b[101] = {0}; // 存第二个多项式的系数,下标则为指数
    int maxA = 0; // 第一个多项式的最高指数
//    int maxB  =0; // 第二个多项式的最高指数

    cout << "请输入第一个多项式的系数和指数,以(0 0)结束,指数的范围在0~101之间" << endl;
    while (true) {
        cout << "请输入系数和指数:";
        int co, index; // co系数 index指数
        cin >> co >> index;
        if (co == 0 && index == 0) {
            break;
        }
        a[index] += co;
        if (index >= maxA) {
            maxA = index;
        }
    }
    cout << "请输入第二个多项式的系数和指数,以(0 0)结束,指数的范围在0~101之间" << endl;
    while (true) {
        cout << "请输入系数和指数:";
        int co, index; // co系数 index指数
        cin >> co >> index;
        if (co == 0 && index == 0) {
            break;
        }
        a[index] += co;
        if (index >= maxA) {
            maxA = index;
        }
    }
    print(a, maxA);
    return 0;
}

多项式加法—链表实现

#include <iostream>

using namespace std;

typedef struct node {
    int co; // 系数
    int index; // 指数
    struct node *next;
} Node, *Link;

// 打印链表
void print(Link head) {
    if (head->next == nullptr) {
        return;
    }
    Link p = head->next;
    // 先处理最高项
    if (p->co < 0) {
        cout << "-";
    }
    if (abs(p->co) != 1){
        cout << abs(p->co);
    }
    cout << "x";
    if (p->index != 1){
        cout << "^" << p->index;
    }
    p=p->next;
    // 处理其他项
    while (p != nullptr) {
        if (p->co == 0) continue;
        else if (p->co > 0) {
            cout << "+";
        } else if (p->co < 0) {
            cout << "-";
        }
        if (p->index == 0) { // 当前p指向指数为0的
            cout << abs(p->co) << endl;
            break;
        }
        if (abs(p->co) != 1) { // 系数的绝对值不等于1时 输出
            cout << abs(p->co);
        }
        cout << "x";
        if (p->index != 1) { // 指数不为1时
            cout << "^" << p->index;
        }
        p = p->next;
    }
    cout << endl;
}

// 构建/插入链表
void createList(Link head, Link num) {
    if (head->next == nullptr) {
        head->next = num;
        num->next = nullptr;
        return;
    }
    // 维护两个节点 一前一后的关系 以便插入
    Link p = head->next;
    Link q = head;
    // 构建链表 按指数从大到小排列
    while (p != nullptr) {
        if (num->index > p->index) { // 当要插入的节点的指数比p节点的指数大时 就该插在p前面q后面
            num->next = p;
            q->next = num;
            return;
        } else if (num->index == p->index) { // 当要插入的节点的指数和p节点的指数相等时 合并系数
            p->co += num->co;
            return;
        }
        q = p;
        p = p->next;
    }
    // 如果到末尾了 还未插入 则为最后一个节点
    q->next = num;
    num->next = nullptr;
}

// 释放函数
void destroy(Link head){

    // 维护p q一前一后的关系
    Link p = head->next;
    Link q = head;

    while(p != nullptr){
        q->next = p->next;
        free(p);
        p = q->next;
    }
    free(q);
}

int main() {
    Link head = (Link) malloc(sizeof(Node));
    head->next = nullptr;
    int a, b;
    cout << "请输入第一个多项式的系数和指数,以(0 0)结束,指数的范围在0~101之间" << endl;
    while (true) {
        cin >> a >> b;
        if (a == 0 && b == 0) {
            break;
        }
        Link num = (Link) malloc(sizeof(Node));
        num->next = nullptr;
        num->co = a;
        num->index = b;
        createList(head, num);
    }
    cout << "第一个多项式为:";
    print(head);
    cout << "请输入第二个多项式的系数和指数,以(0 0)结束,指数的范围在0~101之间" << endl;
    while (true) {
        cin >> a >> b;
        if (a == 0 && b == 0) {
            break;
        }
        Link num = (Link) malloc(sizeof(Node));
        num->next = nullptr;
        num->co = a;
        num->index = b;
        createList(head, num);
    }
    cout << "合并后的多项式为:";
    print(head);
    destroy(head);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值