代码总结
顺序表
可以随机访问,但删除和插入操作需要移动大量数据
顺序表初始化、扩容、插入、查找、删除、遍历的操作
#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;
}