第二、三章 线性表、栈、队列和数组

一、数据结构定义

线性表

  1. 顺序存储(顺序表就是将线性表中的元素按照某种逻辑顺序,依次存储到从指定位置开始的一块连续的存储空间,重点是连续的存储空间。与数组非常接近)
    • 静态分配(数组的大小和空间固定,再加入新的数据会产生溢出)
      #define MaxSize 50 //线性表的最大长度
      typedef struct{
      	ElemType data[MaxSize];
      	int length;
      } SqList
      
    • 动态分配(动态分配不是链式存储,是顺序存储结构,依然是随机存取方式,只是分配的空间大小可以在运行时动态决定)
      typedef struct{
      	ElemType *data;
      	int length;  // 顺序表当前存放的元素个数,即表长
      	int size; // 顺序表预分配的存储容量,即最多可以存放的元素个数。当分配的空间存满数据后,可在内存的另一处申请一个更大的空间,将原顺序表全部拷贝到刚申请的空间,并修改size值,最后将指针指向该空间首地址
      } SqList; 
      
  2. 链式存储
    • 单链表(“单”是指链表结点只存储单个指针)
      typedef struct LinkNode{
      	ElemType data;
      	struct LinkNode *next;
      } LinkNode, *LinkList;
      

      LinkNode*和LinkList是等价的:通常用LinkNode*表示单链表节点的指针变量;用LinkList表示单链表的头指针

    • 双向链表
      typedef struct DLinkNode{
      	ElemType data;
      	struct DLinkNode *prior, *next; // 前驱和后继指针
      } DLinkNode, *DLinkList;
      
    • 循环链表(相比于之前的单链表和双链表,其特点是表中最后一个节点的指针指向头节点,整个链表形成一个环)
      • 循环单链表
        typedef struct LNode{
        	ElemType data;
        	struct LNode *next;
        } LNode, *LinkList;
        
        // 初始化一个循环单链表
        bool InitList(LinkList &L){
        	L = (LNode *) malloc(sizeof(LNode)); // 分配一个头节点
        	if (L==NULL)
        		return false;
        	L->next = L; // 头节点next指向头节点
        	return true;
        }
        
      第二、三章 线性表、栈、队列和数组_第1张图片
      第二、三章 线性表、栈、队列和数组_第2张图片
      • 循环双链表
        typedef struct DNode{
        	ElemType data;
        	struct DNode *prior, *next;
        } DNode, *DLinklist;
        
        // 初始化空的循环双链表
        bool InitDLinkList(DLinkList &L){
        	L = (DNode *) malloc(sizeof(DNode));
        	if (L==NULL)
        		return false;
        	L->prior = L;
        	L->next = L;
        	return true;
        }
        
        第二、三章 线性表、栈、队列和数组_第3张图片
        第二、三章 线性表、栈、队列和数组_第4张图片
    • 静态链表(单链表是用的指针,但是有的编程语言是没有指针这个功能的,可以用数组来代替指针来描述单链表,这种用数组描述的链表就叫静态链表)
      #define MaxSize 10
      typedef struct{
      	ElemType data; // 存储数据元素
      	int next; // 下一个元素的数组下标
      } SLinkList[MaxSize];
      
      第二、三章 线性表、栈、队列和数组_第5张图片
      第二、三章 线性表、栈、队列和数组_第6张图片
      • 优点:增、删操作不需要大量移动元素
      • 缺点:不能随机存取,只能从头节点开始依次往后查找;容量固定不可变
      • 使用场景:不支持指针的低级语言;数据元素数量固定不变的场景(如操作系统的文件分配表FAT)

  1. 栈的顺序存储(静态数组实现,并需要记录栈顶指针)
    #define MaxSize 50
    typedef struct{
    	Elemtype data[MaxSize];
    	int top; //指向栈顶位置
    }SqStack;
    
  2. 共享栈(顺序存储,两个栈共享同一片空间。栈满条件:top0+1==top1)
    #define MaxSize 10
    typedef struct{
    	ElemType data[MaxSize];
    	int top0; // 0号栈栈顶指针
    	int top1; // 1号栈栈顶指针
    } ShStack;
    
  3. 栈的链式存储(入栈就是在头节点后面插入一个元素,出栈就是把头节点下一个元素删除)
    typedef struct LinkNode{
    	ElemType data;
    	struct LinkNode *next;
    } LinkNode, *LiStack;
    
    请添加图片描述

队列

  1. 顺序存储
    #define MaxSize 50 // 定义队列中元素的最大个数
    typedef struct{
    	ElemType data[MaxSize];
    	int front, rear;
    } SqQueue;
    
  2. 链式存储(头指针指向队首节点的前一个节点,方便删除;尾指针指向队尾节点)
    typedef struct LinkNode{
    	ElemType data; 
    	struct LinkNode *next;
    } LinkNode;
    
    typedef struct{
    	LinkNode *front, *rear; // 队列可由队头指针和队尾指针代表整个链表,因此将队头指针和队尾指针放入一个结构体用来表示队列
    } LinkQueue;
    
  3. 循环队列:是解决了假溢出问题的顺序队列
  4. 双端队列:指在队列的两端都可以进行插入和删除操作的队列

数组

无代码

二、代码/算法

线性表

  1. 顺序表上的基本操作:初始化(静态分配and动态分配)、插入、删除、按位查找、按值查找、遍历输出、顺序表清空、顺序表判空、(动态分配)增加动态数组的长度
  2. 单链表上的操作:
    • 单链表的初始化
      • 有头节点
      • 无头节点(不带头节点,写代码更麻烦。对第一个数据节点和后续数据节点的处理需要用不同的代码逻辑,对空表和非空表的处理需要用不同的代码逻辑)
    • 建立单链表
      • 头插法
      • 尾插法
    • 按位查找节点
    • 按值查找节点
    • 单链表遍历输出
    • 插入节点
    • 删除节点
    • 求表长
    • 单链表清空
    • 判空
    • 原地倒置单链表
    • 双指针遍历单链表:双指针的几个经典用法:
      • 找到链表倒数第k个节点
      • 找到链表中间的节点
      • 判断链表是否有环
  3. 双链表上的基本操作:插入、删除
  4. 循环链表的插入和删除与普通链表区别不大,只需在头尾处进行插入删除操作时要注意维护循环链表循环的特性。若

  1. 顺序栈的基本操作:初始化、判空、判满、进栈、出栈、读栈顶元素
  2. 链栈的基本操作:进栈、出栈

队列

  1. 不同队列的操作:初始化、判队空、判队满、入队、出队
    • 链式存储下,一般不会队满,除非内存不足

不同数据结构的对比

  1. 顺序表 VS 链表
    • 逻辑结构:都属于线性表,都是线性结构
    • 物理结构(存储结构)
      • 顺序表是顺序存储,支持随机存取、存储密度高。但大片连续空间分配不方便,改变容量不方便
      • 链表是链式存储,离散的小空间分配方便,改变容量方便。但不可随机存取,存储密度低
    • 数据的运算和操作
      • 创建:
        • 顺序表需要预先分配大片连续空间(静态分配容量不可改变,动态分配容量可改变,但要移动大量元素,时间代价高);
        • 链式存储只需分配一个头节点(也可不要头节点,只声明一个头指针)
      • 销毁:
        • 顺序表:静态分配为系统自动回收空间;动态分配需要手动free(删除)
        • 链表:需要依次free各个节点
      • 插入/删除元素
        • 顺序表:需要将后序元素都后移/前移,时间复杂度 O ( n ) O(n) O(n)(主要来源于移动元素)
        • 链表:只需修改指针,时间复杂度 O ( n ) O(n) O(n)(主要来源于查找目标元素)
      • 查找
        • 顺序表:按位查找 O ( 1 ) O(1) O(1);按值查找 O ( n ) O(n) O(n)
        • 链表:按位查找 O ( n ) O(n) O(n);按值查找 O ( n ) O(n) O(n)
    • 综上,若表长难以预估、经常要增加/删除元素,用链表;若表长可预估、查询(搜索)操作较多则用顺序表

你可能感兴趣的:(数据结构代码相关,数据结构,算法)