线性结构(顺序表、链表、栈和队列)

本文深入探讨了顺序表、链表和栈这三种基本数据结构在编程中的应用。顺序表提供随机访问,但插入和删除效率较低,通过扩容解决空间问题。链表则支持高效插入和删除,但访问速度慢。栈作为后进先出的数据结构,常用于表达式求值和路径查找等。文中通过实例展示了它们的创建、插入、删除和遍历操作,帮助读者理解其工作原理和应用场景。
摘要由CSDN通过智能技术生成

代码总结

顺序表

可以随机访问,但删除和插入操作需要移动大量数据

顺序表初始化、扩容、插入、查找、删除、遍历的操作

#include <stdio.h>
#include <stdlib.h>

#define ERROR 0
#define OK 1

typedef struct Vector 
{
    int size;
    int length;
    int *data;
} Vector;

void init(Vector *vector,int size) 
{
    vector->size=size;
    vector->length=0;
    vector->data=(int *)malloc(sizeof(int)*size);
}

void expand(Vector *vector) 
{
    int *old_data=vector->data;
    vector->size*=2;
    vector->data=(int *)malloc(sizeof(int)*vector->size);
    for(int i=0;i<vector->length;i++)
    {
        vector->data[i]=old_data[i];
    }
    free(old_data);
}

int insert(Vector *vector,int pos,int value)
{
    if(pos<0 || pos>vector->length)
    {
        return ERROR;
    }
    if(vector->length>=vector->size)
    {
        expand(vector);
    }
    for(int i=vector->length;i>pos;i--)
    {
        vector->data[i]=vector->data[i-1];
    }
    vector->data[pos]=value;
    vector->length++;
    return OK;
}

int search(Vector *vector,int value)
{
    for(int i=0;i<vector->length;i++)
    {
        if(vector->data[i]==value)
        {
            return i;
        }
    }
    return -1;
}

int delete_node(Vector *vector,int pos) 
{
    if(pos<0 || pos>=vector->length)
    {
        return ERROR;
    }
    for(int i=pos+1;i<vector->length;i++)
    {
        vector->data[i-1]=vector->data[i];
    }
    vector->length--;
    return OK;
}

void print(Vector *vector) 
{
    for(int i=0;i<vector->length;i++)
    {
        printf("%d",vector->data[i]);
        if(i!=vector->length-1)
        {
            printf(" ");
        }
    }
    printf("\n");
}

void clear(Vector *vector) 
{
    free(vector->data);
    free(vector);
}

int main() {
    Vector *a = (Vector *)malloc(sizeof(Vector));
    init(a, 20);
    int m;
    int t;
    int aa,bb;
    scanf("%d",&m);
    for(int i=0;i<m;i++)
    {
        scanf("%d",&t);
        if(t==1)
        {
            scanf("%d %d",&aa,&bb);
            if(insert(a,aa,bb)==1)
            {
                printf("success\n");
            }
            else
            {
                printf("failed\n");
            }
        }
        else if(t==2)
        {
            scanf("%d",&aa);
            if(delete_node(a,aa)==1)
            {
                printf("success\n");
            }
            else
            {
                printf("failed\n");
            }
        }
        else if(t==3)
        {
            scanf("%d",&aa);
            if(search(a,aa)!=-1)
            {
                printf("success\n");
            }
            else
            {
                printf("failed\n");
            }
        }
        else if(t==4)
        {
            print(a);
        }
    }

    return 0;
}

链表

链表创建、插入、遍历、删除的操作

#include <stdio.h>
#include <stdlib.h>

typedef struct Node
{
    int data;
    struct Node *next;
}Node, *LinkedList;

LinkedList insert(LinkedList head, Node *node, int index) 
{
    if (head == NULL) 
    {
        if (index != 0) 
        {
            return head;
        }
        head = node;
        return head;
    }
    if (index == 0) 
    {
        node->next = head;
        head = node;
        return head;
    }
    Node *current_node = head;
    int count = 0;
    while (current_node->next != NULL && count < index - 1)
    {
        current_node = current_node->next;
        count++;
    }
    if (count == index - 1) 
    {
        node->next = current_node->next;
        current_node->next = node;
    }
    return head;
}

void output(LinkedList head) 
{
    if (head == NULL) 
    {
        return;
    }
    Node *current_node = head;
    while (current_node != NULL) 
    {
        printf("%d ", current_node->data);
        current_node = current_node->next;
    }
    printf("\n");
}

LinkedList delete_node(LinkedList head,int index)
{
    if(head==NULL)
    {
        return head;
    }
    Node *current_node=head;
    int count=0;
    if(index==0)
    {
        head=head->next;
        free(current_node);
        return head;
    }
    while(current_node->next!=NULL && count<index-1)
    {
        current_node=current_node->next;
        count++;
    }
    if(count==index-1 && current_node->next!=NULL)
    {
        Node *delete_node=current_node->next;
        current_node->next=delete_node->next;
        free(delete_node);
    }
    return head;
    
}

void clear(LinkedList head)
{
    Node *current_node = head;
    while (current_node != NULL) 
    {
        Node *delete_node = current_node;
        current_node = current_node->next;
        free(delete_node);
    }
}

int main() {
    LinkedList linkedlist = NULL;
    for (int i = 1; i <= 10; i++) 
    {
        Node *node = (Node *)malloc(sizeof(Node));
        node->data = i;
        node->next = NULL;
        linkedlist = insert(linkedlist, node, i - 1);
    }
    output(linkedlist);
    linkedlist=delete_node(linkedlist,2);
    output(linkedlist);
    clear(linkedlist);
    return 0;
}

链表反转(将整个链表按倒序排列)

1、定义一个用于遍历的指针,初始指向头结点的下一个结点

2、让头结点的next指NULL

3、遍历链表,将遍历指针指向的结点的next指向头结点,同时借助另一个结点保存下一个遍历到的结点,如此循环至链表遍历结束

LinkedList reverse(LinkedList head)
{
    if(head==NULL)
    {
        return head;
    }
    Node *next_node,*current_node;
    current_node=head->next;
    head->next=NULL;
    while(current_node!=NULL)
    {
        next_node=current_node->next;
        current_node->next=head;
        head=current_node;
        current_node=next_node;
    }
    return head;
}

其他链表:

  • 循环链表:相比单链表,循环链表不同的是它将最后一个结点的next指针指向了头结点,循环链表里没有空指针,所以在判断结束条件时,不再是判断指针是否为空,而是判断指针是否等于某固定指针,在单链表里,一个节点只能访问到它后面的结点,而在循环链表里它可以访问到所有的结点
  • 双向链表:单链表里的指针域只记录了结点的下一个结点,也就是后继结点,而双向链表的指针域还记录了结点的上一个结点,也就是前驱结点。

循环链表的插入、删除

对于循环链表的销毁,只需将其拆成单链表再删除即可,循环链表中一般是将head当作尾结点的

约瑟夫问题:

#include <stdio.h>
#include <stdlib.h>

typedef struct Node
{
    int data;
    struct Node *next;
}Node, *LinkedList;

LinkedList insert(LinkedList head, Node *node, int index) //循环链表的插入
{
    if (head == NULL)
    {
        if (index != 0)
        {
            return head;
        }
        head = node;
        head->next=head;
        return head;
    }
    if (index == 0) 
    {
        node->next = head->next;
        head->next = node;
        return head;
    }
    Node *current_node = head->next;
    int count = 0;
    while (current_node != head && count < index - 1)
    {
        current_node = current_node->next;
        count++;
    }
    if (count == index - 1)
    {
        node->next = current_node->next;
        current_node->next = node;
    }  
    if(node==head->next)
    {
        head=node;
    }
    return head;
}

void output_josephus(LinkedList head,int m)//删除环上元素
{
    Node *current_node=head
    head=NULL;
    while(current_node->next!=current_node)
    {
        for(int i=1;i<m;i++)
        {
            current_node=current_node->next;
        }
        printf("%d ",current_node->next->data);
        Node *delete_node=current_node->next;
        current_node->next=current_node->next->next;
        free(delete_node);
    }
    printf("%d\n",current_node->data);
    free(current_node);
}

int main() {
    LinkedList linkedlist = NULL;
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) {
        Node *node = (Node *)malloc(sizeof(Node));
        node->data = i;
        node->next = NULL;
        linkedlist = insert(linkedlist, node, i - 1);
    }
    output_josephus(linkedlist,m);
    return 0;
}

先进后出(FILO)

实现:设置一个变量标记栈顶元素维护栈的次序性,标记初始化为-1

栈的创建、入栈、出栈、获取栈顶元素

表达式求值:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define ERROR 0
#define OK 1

typedef struct Stack 
{
    int *ele;
    int max_len;
    int topi;
} Stack;

void init(Stack *s,int n) 
{
    s->ele=(int *)malloc(sizeof(int)*n);
    s->max_len=n;
    s->topi=-1;
}

int push(Stack *s,int number) 
{
    if(s->topi+1>=s->max_len)
    {
        return ERROR;
    }
    s->topi++;
    s->ele[s->topi]=number;
    return OK;
}

int pop(Stack *s) 
{
    if(s->topi<0)
    {
        return ERROR;
    }
    s->topi--;
    return OK;
}

int top(Stack *s)
{
    if(s->topi<0)
    {
        return -1;
    }
    return s->ele[s->topi];
}

int empty(Stack *s) 
{
    if(s->topi<0)
    {
        return OK;
    }
    else
    {
        return ERROR;
    }
}

int precede(char a,char b) //比较运算符优先级,a:栈顶元素
{
    switch(a)
    {
        case '+':
            if(b=='*' || b=='/')
            {
                return 0;//a<b
            }
            else if(b=='-')
            {
                return 1;
            }
            break;
        case '-':
            if(b=='*' || b=='/')
            {
                return 0;//a<b
            }
            else if(b=='+')
            {
                return 1;
            }
            break;
        case '*':
            return 1;
            break;
        case '/':
            return 1;
            break;
    }
}
int operate(char op,int a,int b) {
    if(op=='+')
    {
        return a+b;
    }
    else if(op=='-')
    {
        return a-b;
    }
    else if(op=='*')
    {
        return a*b;
    }
    else if(op=='/')
    {
        if(b!=0)
        {
            return a/b;
        }
    }
}
void calc(Stack *numbers,Stack *operators) 
{
    int a=top(numbers);
    pop(numbers);
    int b=top(numbers);
    pop(numbers);
    push(numbers,operate(top(operators),b,a));
    pop(operators);
}

void clear(Stack *s)
{
    free(s->ele);
    free(s);
}

int main() {
    int n;
    scanf("%d",&n);
    Stack *numbers=(Stack *)malloc(sizeof(Stack));
    init(numbers,n);
    Stack *operators=(Stack *)malloc(sizeof(Stack));
    init(operators,n);
    char *str=(char *)malloc(sizeof(char)*(n+1));
    scanf("%s",str);
    int i=0;
    while(i<n)
    {
        if(isdigit(str[i]))
        {
            push(numbers,str[i]-'0');
            i++;
        }
        else
        {
            if(empty(operators) || precede(top(operators),str[i])==0)
            {
                push(operators,str[i]);
                i++;
            }
            else
            {
                calc(numbers,operators);
            }
        }
    }
    while(!empty(operators))
    {
        calc(numbers,operators);
    }
    printf("%d",top(numbers));
    clear(numbers);
    clear(operators);
    free(str);
    return 0;
}

单调栈:栈内元素从栈顶到栈底单调递增或单调递减的栈

单调栈例题:给定一个包含若干整数的数组,对于其中每个元素 arri,计算左边离它最近的比 arri 更小的元素。

思路:给定一个包含若干整数的数组,我们从第1个元素开始依次加入单调栈里,并且加入后更新单调栈。那么单调栈有这样的性质:对于从栈顶到栈底单调递减的栈,如果此时栈顶元素为b,加入新元素a后进行更新时,如果a大于b,说明如果从a在数组中的位置开始往左边遍历,则b一定是第一个比a小的元素;如果a小于b,那么对于a右侧的元素来说,b就失去了比较的意义,因此将b从栈中弹出,并继续让a和栈顶元素判断。

单调栈的维护是O(n)级的时间复杂度,因为所有元素只会进出栈各一次。

单调栈的性质:可以在某点左右扩展出一段连续区间,且该点在区间里始终保证是最值

在这里插入图片描述

这道题可以看成点右侧扩展出来的区间,即求右侧第一个大于等于ai的元素,代码如下

#include <stdio.h>
#include <stdlib.h>

#define ERROR 0
#define OK 1

typedef struct Node
{
    int id, height;
} Node;

typedef struct Stack
{
    Node *elements;
    int max_size, top_index;
} Stack;

void init(Stack *s, int length)
{
    s->elements = (Node *)malloc(sizeof(Node) * length);
    s->max_size = length;
    s->top_index = -1;
}

int push(Stack *s, Node element) 
{
    if (s->top_index >= s->max_size - 1) 
    {
        return ERROR;
    }
    s->top_index++;
    s->elements[s->top_index] = element;
    return OK;
}

int pop(Stack *s) 
{
    if (s->top_index < 0) 
    {
        return ERROR;
    }
    s->top_index--;
    return OK;
}

Node top(Stack *s)
{
    return s->elements[s->top_index];
}

int empty(Stack *s) 
{
    if (s->top_index < 0)
    {
        return 1;
    }
    else 
    {
        return 0;
    }
}

void clear(Stack *s) 
{
    free(s->elements);
    free(s);
}

int main() 
{
    int n,ans=0;
    scanf("%d",&n);
    Stack *stack=(Stack *)malloc(sizeof(Stack));
    init(stack,n);
    Node temp;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&temp.height);
        temp.id=i;
        while(!empty(stack) && top(stack).height<=temp.height)//构造从栈顶到栈底单调递增的栈
        {
            ans=ans+i-top(stack).id-1;
            pop(stack);
        }
        push(stack,temp);
    }
    while(!empty(stack))
    {
        ans=ans+n+1-top(stack).id-1;
        pop(stack);
    }
    printf("%d\n",ans);
    clear(stack);
    return 0;
}

总结:

**从栈顶到栈底单调递减的单调栈:**当前元素a若是直接入栈,且当前栈顶元素b不出栈,则栈顶元素b一定是a往左第一个比a小的元素,元素a是b往右第一个比b大的元素; 若b出栈,则b是a往左第一个比a大的元素,a是b往右第一个比b小的元素。

**从栈顶到栈底单调递增的单调栈:**当前元素a若是直接入栈,且当前栈顶元素b不出栈,则栈顶元素b一定是a往左第一个比a大的元素,元素a是b往右第一个比b小的元素; 若b出栈,则b是a往左第一个比a小的元素,a是b往右第一个比b大的元素。

队列

先进先出(FIFO)

实现:设置队首与队尾两个变量维护队列的次序性,队首初始化为0,队尾初始化为-1

队列的创建、入队、遍历、获取队首元素、出队

#include <stdio.h>
#include <stdlib.h>

#define ERROR 0
#define OK 1

typedef struct Queue
{
    int *data;
    int head,tail,length;//队首、队尾、数组长度
}Queue;

void init(Queue *q, int length) 
{
    q->data=(int *)malloc(sizeof(int)*length);
    q->length=length;
    q->head=0;
    q->tail=-1;//队尾标记最后一个元素下标
}

int push(Queue *q, int element) 
{
    if(q->tail+1>=q->length)//判满
    {
        return ERROR;
    }
    q->tail++;
    q->data[q->tail]=element;
    return OK;
}

void output(Queue *q) 
{
    for(int i=q->head;i<=q->tail;i++)
    {
        printf("%d",q->data[i]);
        if(i!=q->tail)
        {
            printf(" ");
        }
    }
    printf("\n");
}

int front(Queue *q)
{
    return q->data[q->head];
}
void pop(Queue *q) 
{
    q->head++;
}
//获取队首元素与出队都需要判空,这一步已在main函数中实现

int empty(Queue *q)
{
    return q->head > q->tail;
}

void clear(Queue *q) 
{
    free(q->data);
    free(q);
}

int main() {
    Queue *queue = (Queue *)malloc(sizeof(Queue));
    init(queue, 100);
    int m,n;
    scanf("%d",&m);
    for(int i=0;i<m;i++)
    {
        int ma;
        scanf("%d",&ma);
        push(queue,ma);
    }
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        if(!empty(queue))
        {
            pop(queue);
        }
    }
    if(!empty(queue))
    {
        printf("%d\n",front(queue));
    }
    output(queue);
    return 0;
}

上述队列实现存在一个问题——“假上溢”,即当队尾到达队列上限时就无法再将元素入队,否则就溢出了,可是当队尾达到队列上限时,若一个队列不断进行入队出队、入队出队时,当队尾到达队列上限时,此时队列其实是空的,却无法再插入元素,这就是假上溢

循环队列:以循环的方式来存储队列,当队尾达到队列上限时,如果队列内元素仍有存储空间,则将队尾跳转到数组开头位置继续进行存储,即0号下标,同样,队首也以此类推,这样一来对于队列空间的利用率就达到了最大,避免了假上溢问题

循环队列的入队、出队与遍历:

#include <stdio.h>
#include <stdlib.h>

#define ERROR 0
#define OK 1

typedef struct Queue 
{
    int *data;
    int head, tail, length, count;//count为实际队内实际元素个数
}Queue;

void init(Queue *q, int length) 
{
    q->data = (int *)malloc(sizeof(int) * length);
    q->length = length;
    q->head = 0;
    q->tail = -1;
    q->count = 0;
}

int push(Queue *q, int element) 
{
    if (q->count >= q->length) 
    {
        return ERROR;
    }
    q->tail = (q->tail + 1) % q->length;
    q->data[q->tail] = element;
    q->count++;
    return OK;
}

void output(Queue *q) 
{
    int i=q->head;
    for(int j=0;j<q->count;j++)
    {
        printf("%d ",q->data[i]);
        i=(i+1)%q->length;
    }
    printf("\n");
}

int front(Queue *q) 
{
    return q->data[q->head];
}

void pop(Queue *q) 
{
    q->head=(q->head+1)%q->length;
    q->count--;
}

int empty(Queue *q)
{
    return q->count==0;
}

void clear(Queue *q) 
{
    free(q->data);
    free(q);
}

int main()
{
    Queue *q = (Queue *)malloc(sizeof(Queue));
    init(q, 100);
    for (int i = 1; i <= 10; i++) 
    {
        push(q, i);
    }
    output(q);
    if (!empty(q)) 
    {
        printf("%d\n", front(q));
        pop(q);        
    }
    output(q);
    clear(q);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值