菜鸟日记之数据结构1

本周学习了数据结构中的几个部分,分别为:顺序表,链表,栈,队列。

顺序表 

顺序存储就是将数据存储到一片连续的内存中,在C语言环境下,可以 是具名的栈数组,或者是匿名的堆数组。

顺序表的设计:
 

1.顺序表设计

一般而言,为了方便操作顺序表,需要一个专门管理顺序表 的"管理结构体",管理结构体一般会包括:

(1) 顺序表总容量

(2) 顺序表当前最末元素下标位置

(3) 顺序表指针

相关代码:

typedef struct
{
    int   capacity; // 顺序表容量
    int   last;     // 最末元素下标
    int * data;     // 顺序表,以整型数据为例
}sequenceList;

顺序表的操作较为简单,下面以一个练习为例来讲解顺序表的操作

顺序表练习

创建一个顺序表,并从键盘接收数字输入,将输入的正整数按从小到大的顺序插入顺序表,并在输入负整数的时候将其绝对值数据删除。每次输入后,将顺序表的内容打印到屏幕上。

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
typedef int datatype;
typedef struct Seqlist
{
    datatype last;//最末元素下标
    datatype *data;//存储顺序表的数据
    datatype capacity;//顺序表容量

} seqlist;
//顺序表的初始化
seqlist *init_seqlist(int cap)
{
    seqlist *s = malloc(sizeof(seqlist));
    if (s == NULL)
        return NULL;
    s->capacity = cap;
    s->last = -1;
    s->data = malloc(sizeof(datatype) * cap);
    if (s->data == NULL)
    {
        free(s);
        return NULL;
    }
    return s;
}
//判断顺序表是否为满
bool isfull(seqlist *list)
{
    if (list->capacity == list->last + 1)
        return true;
    return false;
}
//插入数据
bool insert_data(int data, seqlist *list)
{
    if(list==NULL)
    return false;
    // 判断列表是否全满,若全满则插入不了
    if (isfull(list))
        return false;
    list->data[++list->last] = data;
    return true;
}
//判断顺序表是否为空
bool isempty(seqlist *list)
{
    if (list->last == -1)
    {
        perror("");
        return true;
    }
    return false;
}
//打印顺序表
void printlist(seqlist *list)
{
    if (isempty(list))
        return;
    for (int i = 0; i <= list->last; i++)
    {
        printf("%d\t", list->data[i]);
    }
    printf("\n");
}
//删除顺序表中的元素
void delet_data(seqlist *list, int a)
{
    if(isempty(list))
    return;
    for (int i = 0; i < list->last; i++)
    {
        if(list->data[i] == -a)
        {
            for (int j = i; j < list->last; j++)
            {
                list->data[j]=list->data[j+1];
            }
            
        }
    }
    list->last--;
}
// 销毁顺序表
void destory(seqlist *list)
{
    free(list->data);
    list->data=NULL;
    free(list);
    list=NULL;
}
int main()
{
    int cap = 0;
    scanf("%d", &cap);
    // 初始化链表
    seqlist *list = init_seqlist(cap);
    
    int buf[5]={0};
    printf("请输入元素:");
    for (int i = 0; i < 5; i++)
    {
        scanf("%d", &buf[i]);
    }
    //对输入元素进行排序
    for(int j=0;j<4;j++)
    {
        for(int k=0;k<4-j;k++)
        {
            if(buf[k]>buf[k+1])
            {
                int temp=buf[k];
                buf[k]=buf[k+1];
                buf[k+1]=temp;
            }
        }

    }
    // 插入元素
    for(int m=0;m<5;m++)
    {
        insert_data(buf[m],list);
    }
    // 打印顺序表
    printlist(list);
    // 删除元素
    int a;
    printf("请输入要删除的元素的负数:");
    scanf("%d", &a);
    delet_data(list, a);
    // 打印顺序表
    printlist(list);
    //销毁顺序表
    destory(list);
    return 0;
}

顺序表的优缺点

1.优点

不需要多余的信息来记录数据的关系,存储密度高

所有数据顺序存储在一片连续的内存中,支持立即访问任意一个随机数 据,比如上述顺序表中第i个节点是s->data[i]

2.缺点

插入、删除时需要保持数据的物理位置反映其逻辑关系,需要成片移动数据

当数据节点较多时,需要一整片较大的连续内存空间

当数据节点数量变化剧烈时,内存的释放和分配不灵活

链表

链式存储的线性表,简称链表。

链表其实是由一个或者多个结构体通过指针指向的关系构成
我们把每个结构体的变量称为节点,节点里面由两个成员组成
一个是数据域,另外一个是指针域,指针域是用于存放下一个节点的地址
以此类推,我们把这种存储方式称为链式存储。

链表的基本操作,一般包括:

  1. 节点设计

  2. 初始化空链表

  3. 增删节点

  4. 链表遍历

  5. 销毁链表

 单向链表
无头结点的单向链表

// 节点
struct node
{
    int data; //数据域 
    // 指针域,指向下一个节点(存放下一个节点的地址)
    struct node *next; 
};

不带头结点的单向链表的相关操作:

#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct Node
{
    datatype data;     // 数据域
    struct Node *next; // 指针域
} node;
node *create_newnode(datatype data) // 创建新节点,返回新节点
{
    node *pnew = malloc(sizeof(node)); // 将节点开在堆空间,节点类型实质上非指针,而是定义了一个结构体指针指向该节点,故返回该指针时,别的指针也能通过这个指针找到节点地址
    pnew->data = data;
    pnew->next = NULL;
    return pnew;
}
node *create_list(void) // 创建链表,返回头结点
{
    // 一开始链表为空
    node *pnew = NULL;
    node *first = NULL;
    node *last = NULL;
    while (1)
    {
        datatype data;
        scanf("%d", &data);
        if (data == 0)
            break;
        pnew = create_newnode(data);
        if (pnew == NULL)
            return NULL;
        if (first == NULL && last == NULL) // 空链表
        {
            first = last = pnew;
        }
        else //(头插法)逆序
        {
            pnew->next = first;
            first = pnew;
        }
        // else // (尾插法)正序
        // {
        //     last->next = pnew;
        //     last = pnew; // 更新尾节点指针
        // }
    }
    return first;
}
// 修改节点 为什么需要定义一个新的指针p来遍历链表呢?因为在修改过程中,我们需要遍历整个链表找到所有符合条件的节点,并修改它们的数据。直接使用first来遍历可能会导致遍历过程中改变first指针指向的节点,从而导致链表遍历的中断或错误修改。在这种情况下,如果first->data == olddata成立,会直接修改first指向的节点的数据。这可能会破坏原始链表的结构,导致无法正确遍历整个链表或者漏掉某些节点。
node *update(node *first, datatype olddata, datatype newdata)
{
    node *p = first;
    while (p)
    {
        if (p->data == olddata)
        {
            p->data = newdata;
        }

        p = p->next;
    }
    return first;
}
// 插入节点 同上修改节点的道理
node *insert_node(node *first, datatype olddata, datatype newdata)
{
    node *p1 = first;
    while (p1)
    {
        if (p1->data == olddata)
        {
            node *pnew = create_newnode(newdata);
            pnew->next = p1->next;
            p1->next = pnew;
        }
        p1 = p1->next;
    }

    return first;
}
// 删除节点
node *delete_node(node *first, datatype data)
{
    if (first == NULL)
        return NULL;
    node *p3 = first;
    node *pre = NULL; // 遍历指针的前一个位置,刚开始时,遍历指针在首节点,故前一个为空
    while (p3)
    {
        if (p3->data == data)
        {
            break; // 找到要删除的元素了,则跳出循环
        }
        pre = p3;
        p3 = p3->next;
    } // 若未找到,则遍历完p3为空
    if (p3) // 找到要删除的元素了
    {
        if (p3 == first) // 删除首节点
        {
            first = first->next;
        }
        else // 删除非首节点
            pre->next = p3->next;
        p3->next = NULL;
        free(p3);
        p3 = NULL;
    }

    return first;
}
// 删除多个节点
node *delete_allnode(node *first, datatype data)
{
    if (first == NULL)
        return NULL;
    node *p3 = first;
    node *pre = NULL; // 遍历指针的前一个位置,刚开始时,遍历指针在首节点,故前一个为空
    while (p3)
    {
        while (p3)
        {
            if (p3->data == data)
            {
                break; // 找到要删除的元素了,则跳出循环
            }
            pre = p3;
            p3 = p3->next;
        } // 若未找到,则遍历完p3为空
        if (p3) // 找到要删除的元素了
        {
            if (p3 == first) // 删除首节点
            {
                first = first->next; // 首节点变成后一个节点
                p3->next = NULL;
                free(p3);
                p3 = first; // 依然从首节点开始
            }
            else // 删除非首节点
            {
                pre->next = p3->next;
                p3->next = NULL;
                free(p3);
                p3 = pre->next; // 从下一个节点开始
            }
        }
    }

    return first;
}
// 打印链表 一般不将首节点指针作为边遍历指针
void printlist(node *first)
{
    if (first == NULL)
        return;
    node *p2 = first;
    while (p2)
    {
        printf("%d\t", p2->data);
        p2 = p2->next;
    }
    printf("\n");
}
// 销毁链表
void destory_list(node *first)
{
    node *p4 = first;
    node *temp = NULL;
    while (p4)
    {
        temp = p4->next;
        p4->next = NULL;
        free(p4);
        p4 == temp;
    }
}
int main(int argc, char const *argv[])
{
    // 创建链表
    node *first = create_list();
    // 修改节点
    first = update(first, 4, 44);
    // 打印链表
    printlist(first);
    // 插入节点
    first = insert_node(first, 44, 55);
    // 打印链表
    printlist(first);
    // 删除节点
    first = delete_allnode(first, 1);
    // 打印链表
    printlist(first);
    // 销毁链表
    destory_list(first);
    return 0;
}
带头结点的单向链表

// 定义数据节点
struct node
{
    dataType data; // 数据域
    struct node *next; // 指针域,存放(指向)下一个节点的地址
};
//-----------------------------------

// 定义头节点
struct headNode
{
    struct node *first; // 指向第一个数据节点
    struct node *last; // 指向最后一个数据节点
    int nodeNumber; // 记录链表节点数
};

相关操作代码:增删改查

#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node // 数据节点类型
{
    datatype data;
    struct node *next;
} Node;
typedef struct head // 头结点类型
{
    Node *first;
    Node *last;
    int datanumber; // 节点个数
} Head;
// 初始化头结点
Head *init_head(void)
{
    Head *head = (Head *)malloc(sizeof(Head));
    if (head == NULL)
        return NULL;
    head->datanumber = 0;
    head->first = NULL;
    head->last = NULL;
    return head;
}
// 创建节点
Node *create_newnode(datatype data)
{
    Node *pnew = malloc(sizeof(Node));
    if (pnew == NULL)
    {
        perror("");
    }
    pnew->data = data;
    pnew->next = NULL;
    return pnew;
}
// 尾插法
void addtail(Head *head, Node *pnew)
{
    {
        head->last->next = pnew;
        head->last = pnew;
    }
}
// 头插法
void addhead(Head *head, Node *pnew)
{
    pnew->next = head->first;
    head->first = pnew;
}
// 打印链表
void printlist(Head *head)
{
    if (head == NULL)
    {
        return;
    }
    Node *p = head->first;
    while (p)
    {
        printf("%d\t", p->data);
        p = p->next;
    }
    printf("\n");
}
// 创建链表
Head *create_list(void)
{
    // 初始化头结点
    Head *head = init_head();
    // 创建节点
    printf("enter data:");
    while (1)
    {
        int data = 0;
        scanf("%d", &data);
        if (data == 0)
            break;
        Node *pnew = create_newnode(data);
        if (head->first == NULL && head->last == NULL) // 空链表
            head->first = head->last = pnew;
        else
            addtail(head, pnew);
        // addhead(head, pnew);
        head->datanumber++;
    }
    return head;
}
// 更新数据结点
Head *update_node(Head *head, datatype old_data, datatype new_data)
{
    if (head == NULL)
        return NULL;
    Node *p1 = head->first;
    while (p1)
    {
        if (p1->data == old_data)
        {
            p1->data = new_data;
        }
        p1 = p1->next;
    }
    return head;
}
// 插入数据结点
Head *insert_node(Head *head, datatype old_data, datatype new_data)
{
    if (head == NULL)
        return NULL;
    Node *pnew = create_newnode(new_data);
    Node *p2 = head->first;
    Node *pre = NULL;
    while (p2)
    {
        if (p2->data == old_data)
        {
            break;
        }
        pre = p2;
        p2 = p2->next;
    }
    if (p2)
    {
        // 在首节点
        if (p2 == head->first)
        {
            pnew->next = head->first;
            head->first = pnew;
        }
        // 非首节点
        else
        {
            pre->next = pnew;
            pnew->next = p2;
        }
    }
    return head;
}
// 删除数据结点
Head *delete_node(Head *head, datatype old_data)
{
    if (head == NULL)
        return NULL;
    Node *p3 = head->first;
    Node *pre = NULL;
    while (p3)
    {
        while (p3)
        {
            if (p3->data == old_data)
            {
                break;
            }

            pre = p3;
            p3 = p3->next;
        }
        // 若为首节点
        if (p3)
        {
            if (p3 == head->first)
            {
                head->first = p3->next;
                p3->next = NULL;
                free(p3);
                head->datanumber--;
                p3 = head->first;
            }
            // 若为非首节点
            else
            {
                pre->next = p3->next;
                p3->next = NULL;
                free(p3);
                head->datanumber--;
                p3 = pre->next;
            }
        }
    }
    return head;
}
// 销毁链表
void destory(Head *head)
{
    Node *temp = head->first;
    while (temp)
    {
        head->first = head->first->next;
        temp->next = NULL;
        free(temp);
        temp = head->first;
    }
    head->first = NULL;
    head->last = NULL;
    free(head);
}
int main()
{
    // 创建链表
    Head *head = create_list();
    // 打印链表
    printf("原始链表:");
    printlist(head);
    // 更新节点
    int old_data, new_data;
    printf("请输入原数据和要更新的数据:");
    scanf("%d%d", &old_data, &new_data);
    head = update_node(head, old_data, new_data);
    printf("更新节点后的链表:");
    printlist(head);
    // 添加节点
    printf("请输入原数据和要插入的数据:");
    scanf("%d%d", &old_data, &new_data);
    head = insert_node(head, old_data, new_data);
    printf("添加节点后的链表:");
    printlist(head);
    // 删除节点
    printf("请输入要删除的数据:");
    scanf("%d", &old_data);
    head = delete_node(head, old_data);
    printf("删除节点后的链表:");
    printlist(head);
    // 销毁链表
    destory(head);
    return 0;
}

双向链表

// 创建数据节点
struct node
{
    dataType data;
    struct node *prev;// 指向上一个节点
    struct node *next;// 指向下一个节点
};

// 创建头节点
struct headNode
{
    struct node *first; // 指向首节点
    struct node *last; // 指向最后一个节点
    int nodeNumber; // 记录节点数
};
双向链表的增删改查,代码实现:
 

#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node // 定义数据节点类型
{
    datatype data;
    struct node *next;
    struct node *pre;
} node;
typedef struct head // 定义头节点类型
{
    node *first;
    node *last;
    int datamount;
} Head;
// 初始化头结点
Head *init_head()
{
    Head *head = malloc(sizeof(Head));
    if (head == NULL)
        return NULL;
    head->first = NULL;
    head->last = NULL;
    head->datamount = 0;
    return head;
}
//创建数据结点
node *create_node(datatype data)
{
    node *pnew = malloc(sizeof(node));
    if (pnew == NULL)
        return NULL;
    pnew->data = data;
    pnew->next = NULL;
    pnew->pre = NULL;
    return pnew;
}
// 头插法
void addhead(Head *head, node *pnew)
{
    pnew->next = head->first;
    head->first->pre = pnew;
    head->first = pnew;
}
// 尾插法
void addtail(Head *head, node *pnew)
{
    pnew->pre = head->last;
    head->last->next = pnew;
    head->last = pnew;
}
// 打印链表
void printlist(Head *head)
{
    node *p = head->first;
    while (p)
    {
        printf("%d\t", p->data);
        p = p->next;
    }
    printf("\n");
}
//创建链表
Head *create_list()
{
    // 初始化头结点
    Head *head = init_head();
    printf("enter data: ");
    while (1)
    {
        datatype data = 0;
        scanf("%d", &data);
        if (data == 0)
            break;
        // 创建节点
        node *pnew = create_node(data);
        if (head->first == NULL && head->last == NULL) // 空链表
            head->first = head->last = pnew;
        else // 非空链表
             // 头插法
             // addhead(head, pnew);
             // 尾插法
            addtail(head, pnew);
        head->datamount++;
    }
    return head;
}
//更新数据结点
Head *update_node(Head *head, datatype old_data, datatype new_data)
{
    if (head == NULL)
        return NULL;
    node *p1 = head->first;
    while (p1)
    {
        if (p1->data == old_data)
            p1->data = new_data;
        p1 = p1->next;
    }
    return head;
}
//插入数据结点
Head *insert_node(Head *head, datatype old_data, datatype new_data)
{
    if (head == NULL)
        return NULL;
    node *p1 = head->first;
    node *pre = NULL;
    node *pnew = create_node(new_data);
    while (p1)
    {
        if (p1->data == old_data)
            break;
        pre = p1;
        p1 = p1->next;
    }
    if (p1)
    {
        // 若为头结点
        if (p1 == head->first)
        {
            pnew->next = head->first;
            head->first->pre = pnew;
            pnew = head->first;
        }
        // 若为非头结点
        else
        {
            pnew->next = p1;
            p1->pre = pnew;
            pnew->pre = pre;
            pre->next = pnew;
        }
        head->datamount++;
    }
    return head;
}
//删除数据结点
Head *delete_node(Head *head, datatype old_data)
{
    if (head == NULL)
        return NULL;
    node *p1 = head->first;
    node *pre = NULL;
    while (p1)
    {
        while (p1)
        {
            if (p1->data == old_data)
            {
                break;
            }
            pre = p1;
            p1 = p1->next;
        }
        if (p1)
        {
            // 若为头结点
            if (p1 == head->first)
            {
                head->first = p1->next;
                p1->next = NULL;
                p1->pre = NULL;
                free(p1);
                head->datamount--;
                p1 = head->first;
            }
            // 若为尾结点
            else if (p1 == head->last)
            {
                head->last = p1->pre;
                pre->next = NULL;
                p1->next = NULL;
                p1->pre = NULL;
                free(p1);
                head->datamount--;
                p1 = NULL;
            }
            // 若为中间节点
            else
            {
                pre->next = p1->next;
                p1->next->pre = pre;
                p1->next = NULL;
                p1->pre = NULL;
                free(p1);
                head->datamount--;
                p1 = pre->next;
            }
        }
    }
    return head;
}
//销毁链表
void destroy(Head *head)
{
    if (head == NULL)
    {
        printf("NUlllist\n");
        return;
    }

    node *p = head->first;
    while (p)
    {
        head->first = p->next;
        p->next = NULL;
        p->pre = NULL;
        free(p);
        p = head->first;
    }
    head->first = NULL;
    head->last = NULL;
    free(head);
}
int main(int argc, char const *argv[])
{
    // 创建链表
    Head *head = create_list();
    // 打印链表
    printf("原始链表:");
    printlist(head);
    // 更新节点
    int old_data, new_data;
    printf("请输入原数据和要更新的数据:");
    scanf("%d%d", &old_data, &new_data);
    head = update_node(head, old_data, new_data);
    printf("更新节点后的链表:");
    printlist(head);
    // 添加节点
    printf("请输入原数据和要插入的数据:");
    scanf("%d%d", &old_data, &new_data);
    head = insert_node(head, old_data, new_data);
    printf("添加节点后的链表:");
    printlist(head);
    // 删除节点
    printf("请输入要删除的数据:");
    scanf("%d", &old_data);
    head = delete_node(head, old_data);
    printf("删除节点后的链表:");
    printlist(head);
    // 销毁链表
    destroy(head);
    return 0;
}

双向循环链表

双向循环链表的增删改查,代码实现:
 

#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node // 定义数据节点类型
{
    datatype data;
    struct node *next;
    struct node *pre;
} node;
typedef struct head // 定义头节点类型
{
    node *first;
    node *last;
    int datamount;
} Head;
// 初始化头结点
Head *init_head()
{
    Head *head = malloc(sizeof(Head));
    if (head == NULL)
        return NULL;
    head->first = NULL;
    head->last = NULL;
    head->datamount = 0;
    return head;
}
//创建结点
node *create_node(datatype data)
{
    node *pnew = malloc(sizeof(node));
    if (pnew == NULL)
        return NULL;
    pnew->data = data;
    pnew->next = NULL;
    pnew->pre = NULL;
    return pnew;
}
// 头插法
void addhead(Head *head, node *pnew)
{
    pnew->next = head->first;
    head->first->pre = pnew;
    head->first = pnew;
}
// 尾插法
void addtail(Head *head, node *pnew)
{
    pnew->pre = head->last;
    head->last->next = pnew;
    head->last = pnew;
}
// 打印链表
void printlist(Head *head)
{
    node *p = head->first;
    for (int i = 0; i < head->datamount; i++)
    {
        printf("%d\t", p->data);
        p = p->next;
    }

    printf("\n");
}
//创建链表
Head *create_list()
{
    // 初始化头结点
    Head *head = init_head();
    printf("enter data: ");
    while (1)
    {
        datatype data = 0;
        scanf("%d", &data);
        if (data == 0)
            break;
        // 创建节点
        node *pnew = create_node(data);
        if (head->first == NULL && head->last == NULL) // 空链表
            head->first = head->last = pnew;
        else // 非空链表
             // 头插法
             // addhead(head, pnew);
             // 尾插法
            addtail(head, pnew);
        head->datamount++;
    }
    // 设计链表为循环属性
    if (head->datamount != 0)
    {
        head->first->pre = head->last;
        head->last->next = head->first;
    }
    return head;
}
//更新数据结点
Head *update_node(Head *head, datatype old_data, datatype new_data)
{
    if (head == NULL)
        return NULL;
    node *p1 = head->first;
    for (int i = 0; i < head->datamount; i++)
    {
        if (p1->data == old_data)
            p1->data = new_data;
        p1 = p1->next;
    }

    return head;
}
//插入数据结点
Head *insert_node(Head *head, datatype old_data, datatype new_data)
{
    if (head == NULL)
        return NULL;
    node *p1 = head->first;
    node *pre = NULL;
    node *pnew = create_node(new_data);
    for (int i = 0; i < head->datamount; i++)
    {
        if (p1->data == old_data)
            break;
        pre = p1;
        p1 = p1->next;
    }
    if (p1)
    {
        // 若为头结点
        if (p1 == head->first)
        {
            pnew->next = head->first;
            head->first->pre = pnew;
            pnew = head->first;
        }
        // 若为非头结点
        else
        {
            pnew->next = p1;
            p1->pre = pnew;
            pnew->pre = pre;
            pre->next = pnew;
        }
        head->datamount++;
    }
    return head;
}
//删除数据结点
Head *delete_node(Head *head, datatype old_data) {
    if (head == NULL || head->datamount == 0) {
        return head; 
    }

    node *p1 = head->first;
    node *pre = NULL;
    int n = head->datamount;

    while (n > 0) {
        int inner_n = n;

        while (inner_n > 0) {
            if (p1->data == old_data) {
                if (p1 == head->first) {
                    head->first = p1->next;
                }
                if (p1 == head->last) {
                    head->last = p1->pre;
                }
               //为非首节点
                if (pre != NULL) {
                    pre->next = p1->next;
                }
                //为非尾节点
                if (p1->next != NULL) {
                    p1->next->pre = pre;
                }

                node *temp = p1;
                p1 = p1->next;
                free(temp);   
                head->datamount--;
                break;
            }
           //若没找到,遍历次数也少1,往后移
            pre = p1;
            p1 = p1->next;
            inner_n--;
        }

        n--;
    }
    return head;
}
//销毁链表
void destroy(Head *head)
{
    if (head == NULL)
    {
        printf("NUlllist\n");
        return;
    }

    node *p = head->first;
    while (p)
    {
        head->first = p->next;
        p->next = NULL;
        p->pre = NULL;
        free(p);
        p = head->first;
    }
    head->first = NULL;
    head->last = NULL;
    free(head);
}
int main(int argc, char const *argv[])
{
    // 创建链表
    Head *head = create_list();
    // 打印链表
    printf("原始链表:");
    printlist(head);
    // 更新节点
    int old_data, new_data;
    printf("请输入原数据和要更新的数据:");
    scanf("%d%d", &old_data, &new_data);
    head = update_node(head, old_data, new_data);
    printf("更新节点后的链表:");
    printlist(head);
    // 添加节点
    printf("请输入原数据和要插入的数据:");
    scanf("%d%d", &old_data, &new_data);
    head = insert_node(head, old_data, new_data);
    printf("添加节点后的链表:");
    printlist(head);
    // 删除节点
    printf("请输入要删除的数据:");
    scanf("%d", &old_data);
    head = delete_node(head, old_data);
    printf("删除节点后的链表:");
    printlist(head);
    // // 销毁链表
    // destroy(head);
    return 0;
}

双向链表和循环链表的练习题

2.判断链表是否有环

// 2.判断链表是否有环
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int datatype;
typedef struct node // 定义数据节点类型
{
    datatype data;
    struct node *next;
    struct node *pre;
} node;
typedef struct head // 定义头节点类型
{
    node *first;
    node *last;
    int datamount;
} Head;
// 初始化头结点
Head *init_head()
{
    Head *head = malloc(sizeof(Head));
    if (head == NULL)
        return NULL;
    head->first = NULL;
    head->last = NULL;
    head->datamount = 0;
    return head;
}
node *create_node(datatype data)
{
    node *pnew = malloc(sizeof(node));
    if (pnew == NULL)
        return NULL;
    pnew->data = data;
    pnew->next = NULL;
    pnew->pre = NULL;
    return pnew;
}
// 头插法
void addhead(Head *head, node *pnew)
{
    pnew->next = head->first;
    head->first->pre = pnew;
    head->first = pnew;
}
// 尾插法
void addtail(Head *head, node *pnew)
{
    pnew->pre = head->last;
    head->last->next = pnew;
    head->last = pnew;
}
// 打印链表
void printlist(Head *head)
{
    node *p = head->first;
    for (int i = 0; i < head->datamount; i++)
    {
        printf("%d\t", p->data);
        p = p->next;
    }

    printf("\n");
}
//
Head *create_list()
{
    // 初始化头结点
    Head *head = init_head();
    printf("enter data: ");
    while (1)
    {
        datatype data = 0;
        scanf("%d", &data);
        if (data == 0)
            break;
        // 创建节点
        node *pnew = create_node(data);
        if (head->first == NULL && head->last == NULL) // 空链表
            head->first = head->last = pnew;
        else // 非空链表
             // 头插法
             // addhead(head, pnew);
             // 尾插法
            addtail(head, pnew);
        head->datamount++;
    }
    // 设计链表为循环属性
    if (head->datamount != 0)
    {
        head->first->pre = head->last;
        head->last->next = head->first;
    }
    return head;
}
// 判断链表是否有环
bool Islooplist(Head *head)
{
    node *fast = head->first;
    node *slow = head->first;
    while (fast && slow) 
    {
        fast = fast->next->next; // fast走两步
        slow = slow->next;       // slow走一步
        if (fast == slow)
            return true;
    }
    return false;
}
int main(int argc, char const *argv[])
{
    // 创建链表
    Head *head = create_list();
    // 打印链表
    printf("原始链表:");
    printlist(head);
    // 2.判断链表是否有环
    printf("%d\n",Islooplist(head));
    return 0;
}

合并两个链表,按从小到大排序(拆链表,比较节点,插入节点到新链表)

// 合并两个链表,按从小到大排序(拆链表,比较节点,插入节点到新链表)
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node
{
    datatype data;
    struct node *next;
} node;
typedef struct head
{
    node *first;
    node *last;
    int nodenumber;
} listhead;
// 初始化头结点
listhead *init_headlist()
{
    listhead *head = malloc(sizeof(listhead));
    if (head == NULL)
        return NULL;
    head->first = NULL;
    head->last = NULL;
    head->nodenumber = 0;
    return head;
}
// 创建结点
node *create_node(datatype data)
{
    node *pnew = malloc(sizeof(node));
    if (pnew == NULL)
        return NULL;
    pnew->data = data;
    pnew->next = NULL;
    return pnew;
}
// 尾插法
void addtail(listhead *head, node *pnew)
{
    if (head == NULL || pnew == NULL)
        return;
    if (head->last == NULL)
    {
        head->first = head->last = pnew;
    }
    else
    {
        head->last->next = pnew;
        head->last = pnew;
    }
}
// 创建链表
listhead *create_list()
{
    // 初始化头结点
    listhead *head = init_headlist();
    // 创建结点
    printf("please enter data: ");
    while (1)
    {
        int data = 0;
        scanf("%d", &data);
        if (data == 0)
            break;
        node *pnew = create_node(data);
        // 空链表
        if (head->first == NULL && head->last == NULL)
            head->first = head->last = pnew;
        else
            addtail(head, pnew);
        head->nodenumber++;
    }
    return head;
}
void print_list(listhead *head)
{
    if (head == NULL)
        return;
    node *p = head->first;
    while (p)
    {
        printf("%d\t", p->data);
        p = p->next;
    }
    printf("\n");
}
// 合并两条链表
listhead *mergetwolists(listhead *head1, listhead *head2)
{

    if (head1 == NULL)
        return head2;
    if (head2 == NULL)
        return head1;
    // 创建合并之后的空链表
    // 初始化头结点
    listhead *Head = init_headlist();
    node *temp1 = head1->first;
    node *temp2 = head2->first;
    // 遍历两条链表
    while (temp1 != NULL && temp2 != NULL)
    {
        if (temp1->data < temp2->data)
        {
            node *pnew = create_node(temp1->data);
            addtail(Head, pnew);
            Head->nodenumber++;
            temp1 = temp1->next;
        }
        else
        {
            node *pnew = create_node(temp2->data);
            addtail(Head, pnew);
            Head->nodenumber++;
            temp2 = temp2->next;
        }
    }
    // 剩余结点
    while (temp1 != NULL)
    {
        node *pnew = create_node(temp1->data);
        addtail(Head, pnew);
        Head->nodenumber++;
        temp1 = temp1->next;
    }
    while (temp2 != NULL)
    {
        node *pnew = create_node(temp2->data);
        addtail(Head, pnew);
        Head->nodenumber++;
        temp2 = temp2->next;
    }

    return Head;
}
void destroy(listhead *head)
{
    if (head == NULL)
    {
        printf("NUlllist\n");
        return;
    }

    node *p = head->first;
    while (p)
    {
        head->first = p->next;
        p->next = NULL;
        free(p);
        p = head->first;
    }
    head->first = NULL;
    head->last = NULL;
    free(head);
}
int main(int argc, char const *argv[])
{
    // 创建第一条链表
    listhead *head1 = create_list();
    printf("第一条链表为:");
    print_list(head1);
    // 创建第二条链表
    listhead *head2 = create_list();
    printf("第二条链表为:");
    print_list(head2);
    // 合并两条链表
    listhead *head3 = mergetwolists(head1, head2);
    printf("合并后的链表为:");
    print_list(head3);
    // 销毁链表
    destroy(head1);
    destroy(head2);
    destroy(head3);
    return 0;
}

1.基本概念

栈是一种逻辑结构,是特殊的线性表,特殊在只能在固定一端操作

只要满足上述条件,那么这种特殊的线性表就会呈现出一种"后进先出"的逻辑,这种逻辑就被称为栈,栈在生活中到处可见,比如堆叠的盘子、电梯中的人等等。

顺序栈

顺序栈的练习:
 

使用顺序栈,接收键盘的输入,实现如下功能:

  1. 输入数字时,依次入栈。

  2. 输入字母时,依次出栈。

  3. 每次入栈或者出栈,都将顺序栈中的各个元素输出出来。

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
typedef int datatype;
// 顺序栈结构体
typedef struct stack
{
    datatype *data; // 指向顺序链表的首地址
    int size;       // 栈的容量
    int top;        // 栈顶元素下标
} Stack;
// 初始化栈
Stack *init_stack(int size)
{
    Stack *stack = malloc(sizeof(Stack));
    if (stack == NULL)
        return NULL;
    stack->data = malloc(size * sizeof(datatype));
    if (stack->data == NULL)
        return NULL;
    stack->size = size;
    stack->top = -1;
}
// 判断是否栈满
bool Isfull(Stack *stack)
{
    return stack->size - 1 == stack->top;
}
// 判断栈是否空
bool Isempty(Stack *stack)
{
    return stack->top == -1;
}
// 入栈
bool push_stack(Stack *stack, datatype data)
{
    if (Isfull(stack))
        return false;
    stack->data[++stack->top] = data;
    return true;
}
// 取栈顶元素
bool top(Stack *stack, datatype *data) // 传地址使得调用该函数的函数可以拿到栈顶的值
{
    if (Isempty(stack))
        return false;
    *data = stack->data[stack->top];
    return true;
}
// 出栈
bool pop(Stack *stack, datatype *data)
{
    if (!top(stack, data))
        return false;
    stack->top--;
    return true;
}
// 打印入栈时的栈
void printstack(Stack *stack)
{
    for (int i = 0; i <= stack->top; i++)
    {
        printf("%d\t", stack->data[i]);
    }
    printf("\n");
}
// 打印出栈时的栈
void printstack1(Stack *stack)
{
    for (int i = stack->top; i >= 0; i--)
    {
        printf("%d\t", stack->data[i]);
    }
    printf("\n");
}
int main()
{
    // 初始化栈
    int size = 0;
    printf("enter stack size:");
    scanf("%d", &size);
    Stack *stack = init_stack(size);

    printf("enter data:");
    while (1)
    {
        datatype data;
        int ret = scanf("%d", &data);
        if (ret)
        {
            if (!push_stack(stack, data))
            {
                printf("栈已满,无法入栈");
                continue;
            }
            printstack(stack);
        }
        else
        {
            // 清空输入缓冲区
             while (getchar() != '\n');
            if (!pop(stack, &data))
            {
                printf("栈为空,无法出栈");
                continue;
            }

            printstack1(stack);
        }
    }
    printf("\n");
    return 0;
}

链式栈

链式栈的练习:
使用链式栈,实现十进制转八进制:键盘输入一个十进制数,经过链式栈的相关算法,输出八进制数。

// 使用链式栈,实现十进制转八进制:键盘输入一个十进制数,经过链式栈的相关算法,输出八进制数。
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node
{
    datatype data;
    struct node *next;
} node;
typedef struct linkstack
{
    node *top; // 栈顶指针
    int size;  // 记录链式栈中的元素个数
} linkstack;

// 初始化空栈
linkstack *init_linkstack()
{
    linkstack *stack = malloc(sizeof(linkstack));
    if (stack == NULL)
        return NULL;
    stack->size = 0;
    stack->top = NULL;
    return stack;
}
// 判断栈是否为空
bool Isempty(linkstack *stack)
{
    if (stack == NULL)
        return false;
    return stack->size == 0;
}
// 入栈
bool push_stack(linkstack *stack, datatype data)
{
    // 创建节点,永远头插法插入
    node *pnew = malloc(sizeof(node));
    if (pnew == NULL)
        return false;
    pnew->data = data;
    // 将节点置于栈顶
    pnew->next = stack->top;
    stack->top = pnew;
    stack->size++;
    return true;
}
// 取栈顶元素
bool top_stack(linkstack *stack, datatype *data)
{
    if (Isempty(stack))
        return false;
    *data = stack->top->data;
    return true;
}
// 出栈
bool pop_stack(linkstack *stack, datatype *data)
{
    if (Isempty(stack))
        return false;
    node *temp = stack->top;
    // 将栈顶元素剔除
    stack->top = temp->next;
    temp->next = NULL;
    // 返回栈顶元素,并释放节点
    *data = temp->data;
    free(temp);
    return true;
}
// 打印链式栈
void print_linkstack(linkstack *stack)
{
    for (node *temp = stack->top; temp != NULL; temp = temp->next)
    {
        printf("%d", temp->data);
    }
    printf("\n");
}
int main()
{
    // 初始化空栈
    linkstack *stack = init_linkstack();
    int data;
    printf("enter data:");
    scanf("%d", &data);
    while (1)
    {
        int remainder = data % 8;
        push_stack(stack, remainder);
        data = data / 8;
        if (data == 0)
            break;
    }
    printf("转换后的八进制数为:");
    print_linkstack(stack);
    return 0;
}

队列

1. 基本概念

队列是最常见的概念,日常生活经常需要排队,仔细观察队列会发现,队列是一种逻辑结构,是一种特殊的线性表。特殊在:

  • 只能在固定的两端操作线性表

只要满足上述条件,那么这种特殊的线性表就会呈现一种“先进先出”的逻辑,这种逻辑就被称为队列。

循环队列

与其他的逻辑结构类似,队列可以采用顺序存储形成循环队列,也可以采用链式存储形成链式队列。顺序存储的队列之所以被称为循环队列,是因为可以利用更新队头队尾的下标信息,来循环地利用整个数组,出队入队时也不必移动当中的数据。

满队和空队的约定如下:

  • 当front与rear相等时,队列为空

  • 当(rear+1)%队列的总容量 与front相等时,队列为满

  • 注意: 循环队列中,需要牺牲一个存储位置来区分空队和满队

循环队列的练习:

构建一个顺序存储的循环队列,当用户输入数字时,将数字入队,当用户输入字母时,将队头元素出队。每次操作队列之后,将队列中的元素显示出来。

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
typedef int datatype;
typedef struct queue
{
    datatype *data;//存储队列中的元素
    int front;    // 队头下标
    int rear;     // 队尾下标
    int capacity; // 队的容量
} queue;
// 初始化队列
queue *init_queue(int capacity)
{
    queue *q = malloc(sizeof(queue));
    if (q == NULL)
        return NULL;
    q->capacity = capacity;
    q->data = malloc(capacity * sizeof(datatype));
    q->front = 0;
    q->rear = 0;
    return q;
}
// 判断是否队空
bool Isempty(queue *q)
{
    if (q == NULL)
        return false;
    return q->front == q->rear;
}
// 判断是否队满
bool Isfull(queue *q)
{
    if (q == NULL)
        return false;
    return (q->rear + 1) % q->capacity == q->front;
}
// 入队
bool push_queue(queue *q, datatype data)
{
    if (Isfull(q))
    {
        printf("队满,无法入队\n");
        return false;
    }
    q->data[q->rear] = data;
    q->rear = (q->rear + 1) % q->capacity;
    return true;
}
// 出队
bool pop_queue(queue *q, datatype *data)
{
    if (Isempty(q))
    {
        printf("队空,无元素\n");
        return false;
    }
    *data = q->data[q->front];
    q->front = (q->front + 1) % q->capacity;
    return true;
}
// 打印队列中的元素
void print_queue(queue *q)
{
    if (q == NULL || Isempty(q))
    {
        printf("队空,无元素\n");
        return;
    }
    //求得队头到队尾的元素个数,加上容量确保被除数是正的,q->rear:队列的尾部指针,指向最后一个入队元素的下一个位置(即下一个待入队的位置)。
    // int count = (q->rear - q->front + q->capacity) % q->capacity;
    // int index = q->front;
    // for (int i = 0; i < count; i++)
    // {
    //     printf("%d\t", q->data[index]);
    //     index = (index + 1) % q->capacity;
    // }
    for(int i=q->front;i!=q->rear;i=(i+1)%q->capacity)
    {
         printf("%d\t", q->data[i]);
    }
    printf("\n");
}

int main()
{
    // 初始化队列
    queue *q = init_queue(6);
    // 入队,出队
    printf("enter data:");
    while (1)
    {
        datatype data;
        int ret = scanf("%d", &data);
        if (ret == 1)
        {
            if (!push_queue(q, data))
            {
                printf("无法入队\n");
                continue;
            }
        }
        else
        {
            // 清空输入缓冲区
            while (getchar() != '\n');
            if (!pop_queue(q, &data))
            {
                printf("无法出队\n");
                continue;
            }
        }
        print_queue(q);
    }
    return 0;
}

链式队列

链式队列的组织形式与链表无异,只不过插入删除被约束在固定的两端。为了便于操作,通常也会创建所谓管理结构体,用来存储队头指针、队尾指针、队列元素个数等信息:

链式队列练习:
 

构建一个链式队列,当用户输入数字时,将数字入队,当用户输入字母时,将队头元素出队。每次操作队列之后,将队列中的元素显示出来。

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
typedef int datatype;
// 链式队列节点
typedef struct node
{
    datatype data;
    struct node *next;
} node;
// 链式队列管理结构体
typedef struct queue
{
    node *front;//队头指针
    node *rear;//队尾指针
    int size;//当前队列元素个数
} linkqueue;
// 初始化链式队列
linkqueue *init_queue()
{
    linkqueue *q = malloc(sizeof(linkqueue));
    if (q == NULL)
        return NULL;
    q->front = q->rear = NULL;
    q->size = 0;
    return q;
}
// 判断队列是否为空
bool Isempty(linkqueue *q)
{
    return q->size == 0;
}
// 入队
bool push(linkqueue *q, datatype data)
{
    node *pnew = malloc(sizeof(node));
    if (pnew == NULL)
        return false;
    pnew->data = data;
    pnew->next = NULL;
    if (Isempty(q))
        q->front = q->rear = pnew;
    else
    {
        q->rear->next = pnew;
        q->rear = pnew;
    }
    q->size++;
    return true;
}
// 出队
bool pop(linkqueue *q, datatype *data)
{
    if (Isempty(q))
        return false;
    *data = q->front->data;
    if (q->size == 1)
    {
        q->front == NULL;
        q->rear = NULL;
        free(q->front);
    }
    else
    {
        node *temp = q->front;
        q->front = q->front->next;
        temp->next = NULL;
        free(temp);
    }
    q->size--;
    return true;
}
// 打印队列
void print_queue(linkqueue *q)
{
    if (q == NULL || Isempty(q))
    {
        printf("队空,无法打印\n");
        return;
    }
    for (node *temp = q->front; temp!=NULL; temp = temp->next)
    {
        printf("%d\t", temp->data);
    }
    printf("\n");
}
int main()
{
    // 初始化队列
    linkqueue *q = init_queue();
    // 入队出队
    printf("enter data:");
    while (1)
    {
        datatype data;
        int ret = scanf("%d", &data);
        if (ret)
        {
            if (!push(q, data))
            {
                printf("无法入队\n");
                continue;
            }
        }
        else
        {
            while (getchar() != '\n')
                ;
            if (!pop(q, &data))
            {
                printf("无法出栈\n");
                continue;
            }
        }
        print_queue(q);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值