数据结构 栈与队列

一.队列

操作系统是分时系统,分时系统有队列,给进程排队(优先队列)

先入先出

适合处理具有先后顺序的问题; 【循环队列】树的层序遍历 广度优先搜索 【单调队列】区间的最大(小)值
队列的结构定义:

需要连续的存储区,任意类型的指针(内建类型int ,float,其他类型:struct,里面也可以存放链表)

head:队列头指针;

tail:位置不是固定的,可以指向第一个为空的位置*,也可以指向最后一个位置,根据自己的编程习惯定义;

length:队列总长度;

data_type:根据自己的应用类型定义;

队列的假溢出

存储区域原本还有空间存储,但是却不能再进行插入操作;

循环队列:把最后的位置和第一个位置形成一个圈(求余%运算);

加入count表示存入了多少个元素,当count=length时,队列是真的满了;

队列的插入(tail++,值塞进去)
  1. 判断队列是否已满。实际上是由于队尾标记不断增加,需要判断队尾标记是否大于数组长度。

  2. 更新队尾标记,将新插入元素存入队尾。

队列的遍历
  1. 输出队首标记所在的元素。

  2. 队首标记后移一位。

  3. 若队尾标记和队首标记相等,输出最后一个元素,否则返回步骤 1。

队列的出队(head++)
  1. 比较队尾标记和队首标记的大小,当队首标记大于队尾标记则说明队列为空了,此时出队操作是非法的。

  2. 令队首标记后移一位,队首标记后移即视作原队首出队了。

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct Queue{
    	int *data;//队列的数据区;
    	int head, tail, length;
    }Queue;
    
    #define DEFAULTARG(n, x)(#n[0] ? n + 0 : x)//返回默认参数的宏,当n不为空的时候就返回n,不然就返回x;先把n转换为字符串,判断第0位是否为非0的值,如果是非0的值返回n+0,否则返回x;
    #define init(n)(__init(DEFAULTARG(n, 10)))
    
    Queue *__init(int n){//初始化长度为n的队列,返回的应该是队列的地址,故*init;
    	Queue *p = (Queue *)malloc(sizeof(Queue) * 1);
    	p->data = (int *)malloc(sizeof(int) * n);
    	p->length = n;
    	p->head = p->tail = 0;
    	return p;
    }
    
    int expand(Queue *q){
    	int *temp_data = (int *)realloc(q->data, sizeof(int) * q->length * 2);
    	if(temp_data == NULL) return 0;
    	q->data = temp_data;
    	q->length *= 2;
    	return 1;
    }
    
    int push(Queue *q, int val){
    	if(q->tail == q->length) {
    		if(expand(q) == 0)
    		return 0;
    	}//首先判满;
    	q->data[q->tail] = val;
    	q->tail += 1;
    	return 1;
    }
    
    int empty(Queue *q){
    	return q->head == q->tail;//如果head和tail相同的话,说明队列为空;
    }
    
    int pop(Queue *q){
    	if(empty(q)) return 0;
    	q->head += 1;
    	return 1;
    }
    
    void output(Queue *q){
    	printf("Queue :");
    	for(int i = q->head; i < q->tail; i++){
    		printf("%d ", q->data[i]);
    	}
    	printf("\n");
    	return ;
    }
    void clear(Queue *q){
    	if(q == NULL) return ;//队列本身为空就不必
    	free(q->data);
    	free(q);
    	return ;
    }
    
    int main(){
    	Queue *q = init();
    	int op, val;
    	while(~scanf("%d", &op)){
    		switch(op){
    			case 1:
    				scanf("%d", &val);
    				printf("push return value : %d\n", push(q, val));
    				break;
    			case 2:
    				printf("pop return value : %d\n",pop(q));
    				break;
    			default:
    				fprintf(stderr,"operator error\n");
    				break;
    		}
    		output(q);
    	}
    	clear(q);
    	return 0;
    }
    输出结果:

    2
    pop return value : 0
    Queue :
    1 1
    push return value : 1
    Queue :1 
    1 2
    push return value : 1
    Queue :1 2 
    1 7
    push return value : 1
    Queue :1 2 7 
    1 6
    push return value : 1
    Queue :1 2 7 6 
    2
    pop return value : 1
    Queue :2 7 6 
    2
    pop return value : 1
    Queue :7 6 
    2
    pop return value : 1
    Queue :6 
    2
    pop return value : 1
    Queue :

    二.栈

    栈适合处理完全包含的问题 树的深度遍历 深度优先搜素 【单调栈】临近最大(小)值

    系统栈爆炸:递归次数太多,导致栈爆炸;

    进程是资源分配的最基本单位,线程是执行的最基本单位;一个进程包含多个线程,多个线程之间为什么会相互影响是因为用一个进程的存储空间;线程所对应的资源上限是8兆(栈空间)

    栈的结构定义:

    需要连续的存储区,任意类型的指针(内建类型int ,float,其他类型:struct,里面也可以存放链表)

    size队列总长度;

    top 原来的数据并没有哦改变,只是每次改变的是栈顶指针;

    data_type:根据自己的应用类型定义;

    栈的插入
    1. 判断栈是否已满,能否继续插入元素。

    2. 栈顶标记后移一位。

    3. 把新元素插入到当前栈顶标记的位置。

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct Stack{
    	int *data;
    	int size, top;
    }Stack;
    
    #define DEFAULTARG(n, x)(#n[0] ? n + 0 : x)
    #define init(n)(__init(DEFAULTARG(n, 5)))
    Stack *__init(int);//这样调用函数,不用考虑函数的先后顺序;
    int push(Stack *, int);
    int pop(Stack *);
    int top(Stack *);
    int empty(Stack *);
    void clear(Stack *);
    int expand(Stack *);
    
    int main(){
    	Stack *s = init();
    	int val;
    	while(~scanf("%d",&val)){
    		printf("push value %d to Stack\n", val);
    		push(s, val);
    	}
    	while(!empty(s)){
    		printf("Stack top value : %d\n", top(s));
    		pop(s);
    	}
    	return 0;
    }
    
    Stack *__init(int n){
    	Stack *s = (Stack *)malloc(sizeof(Stack) * 1);
    	s->data = (int *)malloc(sizeof(int) * n);
    	s->top = -1;
    	s->size = n;
    	return s;
    }
    
    int push(Stack *s, int val){
    	if(s->top == s->size - 1 && !expand(s)) return 0;
    	s->top += 1;
    	s->data[s->top] = val;
    	return 1;
    }
    
    int pop(Stack *s){
    	if(empty(s)) return 0;
    	s->top -= 1;
    	return 1;
    }
    
    int top(Stack *s){
    	if(empty(s)) return 0;
    	return s->data[s->top];
    }
    
    int empty(Stack *s){
    	return s->top == -1;	
    }
    
    void clear(Stack *s){
    	if(s == NULL) return ;
    	free(s->data);
    	free(s);
    	return ;
    }
    
    int expand(Stack *s){
    	int *p = (int *)realloc(s->data, s->size * 2);
    	if(p == NULL) return 0;
    	s->data = p;
    	s->size = s->size * 2;
    	return 1;
    }
    输出结果:

    注意:输出的时候需要用in文件,由于in文件有结尾,可以使while(~scanf())有终止,普通的输入是没有的!

    bogon:~ hanxu$ ./a.out < in
    push value 1 to Stack
    push value 2 to Stack
    push value 3 to Stack
    push value 4 to Stack
    push value 5 to Stack
    push value 6 to Stack
    push value 7 to Stack
    push value 8 to Stack
    push value 9 to Stack
    push value 10 to Stack
    push value 0 to Stack
    push value 1 to Stack
    push value 3 to Stack
    Stack top value : 3
    Stack top value : 1
    Stack top value : 0
    Stack top value : 10
    Stack top value : 9
    Stack top value : 8
    Stack top value : 7
    Stack top value : 6
    Stack top value : 5
    Stack top value : 4
    Stack top value : 3
    Stack top value : 2
    Stack top value : 1
    栈实现数列反转
    1. 将一个数列的元素依次压入到栈中。

    2. 将栈顶元素出栈。

    3. 判断栈是否为空,不为空则回到步骤 2。

    两个栈实现表达式求值
    1. 使用两个栈分别存储数值和运算符。

    2. 读取表达式字符,数值存入数值栈,运算符和栈顶运算符比较优先级。

    3. 通过运算符优先级不同选择将它压入栈或取出数值栈中两个元素进行计算,计算结果入栈。

    4. 返回步骤 2,直至表达式全部读完。

    5. 弹出一个运算符和两个数值进行运算,计算结果存储数值栈。

    6. 当运算符栈不为空时,返回步骤 5,否则数值栈中剩余的最后一个元素就是表达式求值结果。


猜你喜欢

转载自blog.csdn.net/qq_38362049/article/details/81042526