数据结构&算法-3.受限线性表(黑马学习笔记)

3.1栈

在线性表表尾进行插入和删除操作,表尾指栈顶,而不是栈底。特殊之处在于限制了这个线性表的插入和删除的位置,它始终只在栈顶进行,也使得:栈底是固定的,最先进栈的只能在栈底。

1 栈的顺序存储 ---- 利用数组
设计思路
1.1 栈 属于 先进后出的数据结构
 //栈的结构体
 struct SStack
 {
     void * data[MAX]; //数组
 ​
     //栈的元素个数
     int m_Size;
 };
1.2 初始化栈 init
 //初始化栈
 seqStack init_SeqStack()
 {
     struct SStack * stack = malloc(sizeof(struct SStack));
 ​
     if (stack == NULL)
     {
         return NULL;
     }
 ​
     //清空数组中的每个元素
     memset(stack->data, 0, sizeof(void*)*MAX);
 ​
     stack->m_Size = 0;
 ​
     return stack;
 }
1.3 入栈 push
 //入栈
 void push_SeqStack( seqStack stack, void * data)
 {
     if (stack == NULL)
     {
         return;
     }
     if (data == NULL)
     {
         return;
     }
 ​
     //判断是否已经栈满 ,如果满了 不可以再入栈了
     struct SStack * myStack = stack;
     if (myStack->m_Size == MAX)
     {
         return;
     }
 ​
     myStack->data[myStack->m_Size] = data; //入栈  尾插
 ​
     myStack->m_Size++; //更新栈大小
 }
1.4 出栈 pop
 //出栈
 void pop_SeqStack(seqStack stack)
 {
     if (stack == NULL)
     {
         return;
     }
 ​
     //如果是空栈 不执行出栈
     struct SStack * myStack = stack;
     if (myStack->m_Size <= 0)
     {
         return;
     }
 ​
     //执行出栈
     myStack->data[myStack->m_Size - 1] = NULL;
     //更新栈的大小
     myStack->m_Size--;
 }
1.5 访问栈顶元素 top
 //获取栈顶元素
 void * top_SeqStack(seqStack stack)
 {
 ​
     if (stack == NULL)
     {
         return NULL;
     }
 ​
     struct SStack * myStack = stack;
 ​
     //如果是空栈   返回 NULL
     if (myStack->m_Size == 0)
     {
         return NULL;
     }
 ​
     return myStack->data[myStack->m_Size - 1];
 ​
 }
1.6 返回栈大小 size
//栈的大小
 int size_SeqStack(seqStack stack)
 {
     if (stack == NULL)
     {
         return -1;
     }
 ​
     struct SStack * myStack = stack;
 ​
     return myStack->m_Size;
 }
1.7 判断栈是否为空 isEmpty
 //判断栈是否为空
 int isEmpty_SeqStack(seqStack stack)
 {
     if (stack == NULL)
     {
         return -1; //真
     }
 ​
     struct SStack * myStack = stack;
     if (myStack->m_Size <= 0)
     {
         return 1; //真
     }
 ​
     return  0; //返回假 代表不是空栈
 }
1.8 销毁栈 destroy
//销毁栈
 void destroy_SeqStack(seqStack stack)
 {
 ​
     if (stack == NULL)
     {
         return;
     }
 ​
     free(stack);
     stack = NULL;
 }
1.9测试
 //测试
 struct Person
 {
     char name[64];
     int age;
 };
 void test01()
 {
 ​
     //准备出数据
     struct Person p1 = {  "aaa", 10 };
     struct Person p2 = {  "bbb", 20 };
     struct Person p3 = {  "ccc", 30 };
     struct Person p4 = {  "ddd", 40 };
     struct Person p5 = {  "eee", 50 };
 ​
     //初始化栈
     seqStack stack = init_SeqStack();
 ​
     //入栈
     push_SeqStack(stack, &p1);
     push_SeqStack(stack, &p2);
     push_SeqStack(stack, &p3);
     push_SeqStack(stack, &p4);
     push_SeqStack(stack, &p5);
 ​
     while ( isEmpty_SeqStack(stack) == 0 ) //如果栈不为空  进行访问栈顶元素,并且出栈
     {
         struct Person  * pTop = top_SeqStack(stack); //栈顶元素
 ​
         printf("栈顶元素 姓名: %s  年龄:  %d\n", pTop->name, pTop->age);
 ​
         //出栈
         pop_SeqStack(stack);
     }
 ​
     //栈的大小
     int size = size_SeqStack(stack);
 ​
     printf("栈的大小为:%d\n", size);
 ​
 ​
     //销毁栈
     destroy_SeqStack(stack);
 }
 ​
 ​
 int main(){
 ​
     test01();
 ​
     system("pause");
     return EXIT_SUCCESS;
 }
2 栈的链式存储 ---- 利用链表
设计思路

和顺序存储的对外接口完全一

 #include<stdio.h>
 #include<string.h>
 #include<stdlib.h>
 ​
 struct StackNode
 {
     struct StackNode * next; //只维护指针域
 };
 //链式的栈结构体
 struct LStack
 {
     struct StackNode pHeader; //头节点
 ​
     int m_Size; //栈的大小
 };
 typedef void * LinkStack;
 //初始化栈
 LinkStack init_LinkStack()
 {
     struct LStack * myStack =  malloc(sizeof(struct LStack));
 ​
     if ( myStack == NULL)
     {
         return NULL;
     }
     myStack->pHeader.next = NULL;
     myStack->m_Size = 0;
     return myStack;
 }
 //入栈
 void push_LinkStack( LinkStack  stack , void * data)
 {
     if ( stack == NULL)
     {
         return;
     }
     if ( data == NULL)
     {
         return;
     }
     //入栈 就是 头插
     struct LStack * myStack = stack;
     //拿到用户数据的前四个字节
     struct StackNode * myNode = data;
     //插入节点
     myNode->next = myStack->pHeader.next;
     myStack->pHeader.next = myNode;
 ​
     //更新栈的大小
     myStack->m_Size++;
 }
 //出栈
 void pop_LinkStack( LinkStack stack )
 {
     if (stack == NULL)
     {
         return;
     }
     struct LStack * myStack = stack;
     //如果是空栈   不出栈了
     if ( myStack->m_Size <= 0 )
     {
         return;
     }
     //保存第一个有数据的节点  栈顶元素
     struct StackNode * pFirst = myStack->pHeader.next;
 ​
     myStack->pHeader.next = pFirst->next;
 ​
     //更新链表长度  栈大小
     myStack->m_Size--;
 }
 //返回栈顶元素
 void * top_LinkStack(LinkStack stack)
 {
     if ( stack == NULL)
     {
         return NULL;
     }
     struct LStack * myStack = stack;
     if (myStack->m_Size <= 0 )
     {
         return NULL;
     }
     return myStack->pHeader.next; //将第一个有数据的节点返回就可以了
 }
 //返回栈大小
 int size_LinkStack(LinkStack stack)
 {
     if (stack == NULL)
     {
         return -1;
     }
     struct LStack * myStack = stack;
     return myStack->m_Size;
 }
 //判断是否为空
 int isEmpty_LinkStack(LinkStack stack)
 {
     if (stack == NULL)
     {
         return -1;
     }
     struct LStack * myStack = stack;
     if (myStack->m_Size <= 0 )
     {
         return 1; 
     }
     return 0;
 }
 //销毁
 void destroy_LinkStack(LinkStack stack)
 {
     if (stack == NULL)
     {
         return ;
     }
     free(stack);
     stack = NULL;
 }
 //测试
 struct Person
 {
     struct StackNode node;
     char name[64];
     int age;
 };
 void test01()
 {
     //准备出数据
     struct Person p1 = { NULL,"aaa", 10 };
     struct Person p2 = { NULL,"bbb", 20 };
     struct Person p3 = { NULL,"ccc", 30 };
     struct Person p4 = { NULL,"ddd", 40 };
     struct Person p5 = { NULL,"eee", 50 };
     //初始化栈
     LinkStack stack = init_LinkStack();
     //入栈
     push_LinkStack(stack, &p1);
     push_LinkStack(stack, &p2);
     push_LinkStack(stack, &p3);
     push_LinkStack(stack, &p4);
     push_LinkStack(stack, &p5);
     
     while (isEmpty_LinkStack(stack) == 0) //如果栈不为空  进行访问栈顶元素,并且出栈
     {
         struct Person  * pTop = top_LinkStack(stack); //栈顶元素
         printf("栈顶元素 姓名: %s  年龄:  %d\n", pTop->name, pTop->age);
         //出栈
         pop_LinkStack(stack);
     }
     //栈的大小
     int size = size_LinkStack(stack);
     printf("栈的大小为:%d\n", size);
     //销毁栈
     destroy_LinkStack(stack);
 }
 ​
 int main(){
 ​
     test01();
 ​
     system("pause");
     return EXIT_SUCCESS;
 }
3 栈的应用- 就近匹配案例
设计思路

3.1 扫描字符串

3.2 准备栈

3.3 如果是左括号 ---- 入栈

3.4 如果是右括号

3.4.1 栈是否为空

3.4.2 如果不为空 出栈

3.4.3 如果为空 立即停止 并且报错

3.5 遍历所有字符后 ,判断栈是否为空

3.5.1 如为空 没问题

3.5.2 如果不为空 报错 左括号没有匹配到对应的右括号

3.6 销毁栈

 #define _CRT_SECURE_NO_WARNINGS
 #include<stdio.h>
 #include<string.h>
 #include<stdlib.h>
 #include "seqStack.h"
 ​
 /*
 从第一个字符开始扫描
 当遇见普通字符时忽略,
 当遇见左括号时压入栈中
 当遇见右括号时从栈中弹出栈顶符号,并进行匹配
 匹配成功:继续读入下一个字符
 匹配失败:立即停止,并报错
 结束:
 成功: 所有字符扫描完毕,且栈为空
 失败:匹配失败或所有字符扫描完毕但栈非空
 */
 ​
 //判断字符是否是左括号
 int IsLeft(char ch)
 {
     return ch == '(';
 }
 ​
 //判断字符是否是右括号
 int IsRight(char ch)
 {
     return ch == ')';
 }
 ​
 void printError(char * str, char * errMsg,char * pos)
 {
     printf("错误的信息:%s\n", errMsg);
     printf("%s\n", str);
 ​
     //计算打印空格数量
     int num = pos - str;
     for (int i = 0; i < num; i++)
     {
         printf(" ");
     }
     printf("A\n");
 }
 ​
 void test01()
 {
     char * str = "5+5*(6)+9/3*1-(1+3(";
 ​
     char * p = str;
 ​
     //初始化栈
     seqStack stack  =  init_SeqStack();
 ​
     while ( *p != '\0')
     {
         //如果是左括号  入栈
         if (IsLeft(*p))
         {
             push_SeqStack(stack, p);
         }
         //如果是右括号  弹出栈中的栈顶元素
         if (IsRight(*p))
         {
             //如果栈的元素个数>0 说明 可以匹配
             if (size_SeqStack(stack)>0)
             {
                 //弹出栈顶元素
                 pop_SeqStack(stack);
             }
             else //空栈  匹配失败
             {
                 printError(str, "右括号没有匹配到左括号",p);
                 break;
             }
         }
         p++;
     }
 ​
     //判断栈是否为空栈
     while ( size_SeqStack(stack) > 0)
     {
         printError(str, "左括号没有匹配到右括号", top_SeqStack(stack));
         //弹出栈顶元素
         pop_SeqStack(stack);
     }
 ​
     //销毁栈
     destroy_SeqStack(stack);
     stack = NULL;
 ​
 }
 ​
 int main(){
 ​
     test01();
 ​
     system("pause");
     return EXIT_SUCCESS;
 }
4 中缀表达式和后缀表达式
4.1 中缀是符合人类习惯
4.2 后缀符合计算机使用
4.3 中缀如何转为后缀表达式
4.4 计算机如果利用后缀表达式进行计算
3.2队列

基本概念

  • 队列是一种特殊的受限制的线性表
  • 队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表
  • 队列是一种先进先出的线性表,简称FIFO。允许插入的一端为队尾,允许删除的一端为队头,队列不允许在中间部位进行操作。
1 队列---顺序存储
1.1 利用写好的动态数组实现 顺序存储的队列数据结构
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 ​
 //动态数组结构体
 struct dynamicArray
 {
     void ** pAddr; //维护在堆区真实数组指针
 ​
     int m_capacity; //数组容量
 ​
     int m_size; //数组大小
 };
 ​
 //初始化数组
 struct dynamicArray * init_DynamicArray(int capacity)
 {
     if (capacity <= 0)
     {
         return NULL;
     }
 ​
     struct dynamicArray * array = malloc(sizeof(struct dynamicArray));
 ​
     //判断内存是否申请成功
     if (array == NULL)
     {
         return  NULL;
     }
 ​
     //设置容量
     array->m_capacity = capacity;
     //设置大小
     array->m_size = 0;
     //维护在堆区数组的指针
     array->pAddr = malloc(sizeof(void*)*array->m_capacity);
 ​
     return array;
 }
 ​
 //插入功能
 void insert_dynamicArray(struct dynamicArray * array, int pos, void * data)
 {
     if (array == NULL)
     {
         return;
     }
     if (data == NULL)
     {
         return;
     }
     if (pos < 0 || pos > array->m_size)
     {
         //无效的位置  进行尾插
         pos = array->m_size;
     }
 ​
     //先判断是否已经 满载 ,如果满载了  动态开辟
     if (array->m_size >= array->m_capacity)
     {
         //1、申请一个更大的内存空间
         int newCapacity = array->m_capacity * 2;
 ​
         //2、创建新空间
         void ** newSpace = malloc(sizeof(void *)* newCapacity);
 ​
         //3、将原有数据 拷贝到新空间下
         memcpy(newSpace, array->pAddr, sizeof(void*)*array->m_capacity);
 ​
         //4、释放原有空间
         free(array->pAddr);
 ​
         //5、更改指针指向
         array->pAddr = newSpace;
         //6、更新新容量大小
         array->m_capacity = newCapacity;
 ​
     }
 ​
     //插入新数据元素
     //从最后一个位置开始 依次移动数据  后移
 ​
     for (int i = array->m_size - 1; i >= pos; i--)
     {
         array->pAddr[i + 1] = array->pAddr[i];
     }
 ​
     //将新元素插入到指定位置
     array->pAddr[pos] = data;
 ​
     //更新大小
     array->m_size++;
 }
 ​
 ​
 //遍历数组
 void foreach_DynamicArray(struct dynamicArray * array, void(*myForeach)(void *))
 {
 ​
     if (array == NULL)
     {
         return;
     }
     if (myForeach == NULL)
     {
         return;
     }
 ​
     for (int i = 0; i < array->m_size; i++)
     {
         myForeach(array->pAddr[i]);
     }
 }
 ​
 //删除数组中元素  -- 按照指定位置进行删除
 void removeByPos_DynamicArray(struct dynamicArray * arr, int pos)
 {
     if (arr == NULL)
     {
         return;
     }
     if (pos < 0 || pos > arr->m_size - 1)
     {
         //无效的位置 直接return
         return;
     }
 ​
     //从pos位置开始  到数组尾  数据进行前移
     for (int i = pos; i < arr->m_size - 1; i++)
     {
         arr->pAddr[i] = arr->pAddr[i + 1];
     }
 ​
     arr->m_size--;
 }
 ​
 //删除数组中的元素  --- 按照值来进行删除
 void removeByValue_DynamicArray(struct dynamicArray * arr, void * data, int(*myCompare)(void *, void *))
 {
     if (arr == NULL)
     {
         return;
     }
     if (data == NULL)
     {
         return;
     }
 ​
     for (int i = 0; i < arr->m_size; i++)
     {
         if (myCompare(arr->pAddr[i], data))
         {
             removeByPos_DynamicArray(arr, i);
             break;
         }
     }
 ​
 }
 ​
 //销毁数组
 void destroy_DynamicArray(struct dynamicArray * arr)
 {
     if (arr == NULL)
     {
         return;
     }
 ​
     if (arr->pAddr != NULL)
     {
         free(arr->pAddr);
         arr->pAddr = NULL;
     }
 ​
     free(arr);
     arr = NULL;
 }
1.2 对外提供的接口
1.3 初始化队列
 //初始化队列
 seqQueue init_SeqQueue()
 {
     struct dynamicArray * arr =  init_DynamicArray(MAX);
 ​
     return arr;
 }
1.4 入队
 //入队
 void push_SeqQueue(seqQueue queue, void * data)
 {
     if (queue == NULL)
     {
         return;
     }
     if (data == NULL)
     {
         return;
     }
     struct dynamicArray * myQueue = queue;
     if (myQueue->m_size >= MAX)
     {
         return;
     }
 ​
     //入队 === 尾插
     insert_dynamicArray(myQueue, myQueue->m_size, data);
 ​
 }
1.5 出队
 //出队
 void pop_SeqQueue(seqQueue queue)
 {
     if (queue == NULL)
     {
         return;
     }
 ​
     struct dynamicArray * myQueue = queue;
 ​
     if (myQueue->m_size <= 0)
     {
         return;
     }
 ​
     removeByPos_DynamicArray(myQueue, 0);
 ​
 }
1.6 返回队头
 //返回队头元素
 void * front_SeqQueue(seqQueue queue)
 {
     if (queue == NULL)
     {
         return NULL;
     }
 ​
     struct dynamicArray * myQueue = queue;
 ​
     return myQueue->pAddr[0];
 ​
 }
1.7 返回队尾
 //返回队尾元素
 void * back_SeqQueue(seqQueue queue)
 {
     if (queue == NULL)
     {
         return NULL;
     }
 ​
     struct dynamicArray * myQueue = queue;
     return myQueue->pAddr[myQueue->m_size - 1];
 ​
 }
1.8 返回队列大小
 //返回队列大小
 int size_SeqQueue(seqQueue queue)
 {
     if (queue == NULL)
     {
         return -1;
     }
     struct dynamicArray * myQueue = queue;
     return myQueue->m_size;
 }
1.9 销毁队列
 //销毁
 void destroy_SeqQueue(seqQueue queue)
 {
     
     if (queue == NULL)
     {
         return;
     }
 ​
     destroy_DynamicArray(queue);
 ​
 }
1.10 循环队列判断队列满的条件
(q->rear+1)%MaxSize==q->front
2 队列---链式存储
2.1 和顺序存储对外接口一致
2.2 利用链表方式实现
2.3 实现出一种 符合 先进先出的数据结构
 #include<stdio.h>
 #include<string.h>
 #include<stdlib.h>
 ​
 //链表节点
 struct QueueNode
 {
     struct QueueNode * next; //只维护指针域
 };
 ​
 //队列结构体
 struct LQueue
 {
     //头节点
     struct QueueNode pHeader;
 ​
     //队列大小
     int m_Size;
 ​
     //维护尾节点的指针
     struct QueueNode * pTail;
 ​
 };
 ​
 typedef void * LinkQueue;
 ​
 //初始化队列
 LinkQueue init_LinkQueue()
 {
     struct LQueue * myQueue = malloc(sizeof(struct LQueue));
 ​
     if (myQueue == NULL)
     {
         return NULL ;
     }
 ​
     myQueue->pHeader.next = NULL;
 ​
     myQueue->m_Size = 0;
 ​
     myQueue->pTail = &myQueue->pHeader; //尾节点初始化 就是在头节点
 ​
     return myQueue;
 ​
 }
 ​
 //入队
 void push_LinkQueue(LinkQueue queue, void * data)
 {
     if (queue == NULL)
     {
         return;
     }
     if (data == NULL)
     {
         return;
     }
 ​
     //还原真实队列结构体
     struct LQueue * myQueue = queue;
 ​
     //取出用户数据的前四个字节
     struct QueueNode * myNode = data;
 ​
     //插入新的节点 
     myQueue->pTail->next = myNode;
     myNode->next = NULL;
 ​
     myQueue->pTail = myNode;
 ​
     //更新队列长度
     myQueue->m_Size++;
 }
 ​
 //出队
 void pop_LinkQueue(LinkQueue queue)
 {
     if (queue == NULL)
     {
         return;
     }
 ​
     struct LQueue * myQueue = queue;
 ​
     if (myQueue->m_Size <= 0)
     {
         return;
     }
 ​
     if (myQueue->m_Size == 1)
     {
         myQueue->pHeader.next = NULL;
         myQueue->pTail = &myQueue->pHeader;
         myQueue->m_Size = 0;
 ​
         return;
     }
 ​
     //第一个有数据的节点
     struct QueueNode * pFirst = myQueue->pHeader.next;
 ​
     //更新指针的指向
 ​
     myQueue->pHeader.next = pFirst->next;
 ​
     //更新队列长度
     myQueue->m_Size--;
 ​
 }
 ​
 //返回队头
 void * front_LinkQueue(LinkQueue queue)
 {
     if (queue == NULL)
     {
         return NULL;
     }
 ​
     struct LQueue * myQueue = queue;
 ​
     return myQueue->pHeader.next;
 ​
 }
 ​
 //返回队尾
 void * back_LinkQueue(LinkQueue queue)
 {
     if (queue == NULL)
     {
         return NULL;
     }
 ​
     struct LQueue * myQueue = queue;
 ​
     return myQueue->pTail;
 }
 ​
 //返回队列大小
 int size_LinkQueue(LinkQueue queue)
 {
     if (queue == NULL)
     {
         return -1;
     }
 ​
     struct LQueue * myQueue = queue;
 ​
     return myQueue->m_Size;
 ​
 }
 ​
 //销毁
 void destroy_LinkQueue(LinkQueue queue)
 {
     if (queue == NULL)
     {
         return;
     }
 ​
 ​
     free(queue);
     queue = NULL;
 ​
 }
 ​
 //测试队列
 struct Person
 {
     void * node;
     char name[64];
     int age;
 };
 ​
 void test01()
 {
     //初始化队列
     LinkQueue queue = init_LinkQueue();
     //准备数据
     struct Person p1 = { NULL, "aaa", 10 };
     struct Person p2 = { NULL, "bbb", 20 };
     struct Person p3 = { NULL, "ccc", 30 };
     struct Person p4 = { NULL, "ddd", 40 };
     struct Person p5 = { NULL, "eee", 50 };
     //入队
     push_LinkQueue(queue, &p1);
     push_LinkQueue(queue, &p2);
     push_LinkQueue(queue, &p3);
     push_LinkQueue(queue, &p4);
     push_LinkQueue(queue, &p5);
 ​
     while (size_LinkQueue(queue) > 0)
     {
         //获取队头元素
         struct Person * pFront = front_LinkQueue(queue);
         printf("队头元素--- 姓名: %s  年龄: %d\n", pFront->name, pFront->age);
 ​
         //获取队尾
         struct Person * pBack = back_LinkQueue(queue);
         printf("队尾元素--- 姓名: %s  年龄: %d\n", pBack->name, pBack->age);
 ​
         //出队
         pop_LinkQueue(queue);
     }
 ​
     printf("::::队伍大小为: %d\n", size_LinkQueue(queue));
 ​
 ​
     //销毁
     destroy_LinkQueue(queue);
 ​
 ​
 }
 ​
 int main(){
 ​
     test01();
 ​
     system("pause");
     return EXIT_SUCCESS;
 }


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值