数据结构备战

数据结构常考知识点

一、基础知识

1.数据结构与算法概念

  • 数据的基本单位(数据元素)
  • 数据元素由(数据项)组成
  • 数据元素的不可分割的最小单位(数据项)
  • 数据对象是相同性质的(数据元素)的集合,是(数据)的子集
  • 数据类型是一个(值)的集合和定义在此集合上的一组(操作)的总称
  • 数据类型(原子类型、结构类型、抽象数据类型)
  • 数据结构是相互之间存在一种或多种特定关系的(数据元素)的集合
  • 逻辑结构分为(线性结构、集合、树形结构、图形结构)
  • 存储结构分为(顺序存储、链式存储、索引存储、散列存储)
  • 数据运算是对(数据)实施的操作
  • 算法是对特定问题(求解步骤)的描述,是(指令)的有限序列
  • 算法具有的特性(有穷性、确定性、可行性、有输入、有输出)
  • 算法设计的目标(正确性、可使用性、可读性、健壮性、高效率与低存储量需求)
  • 大O表示法通常表示运算时间的(渐进上界)

2.线性表

  • 线性表是具有相同特性的(数据元素)的有限序列

  • 顺序表的数据结构

    typedef struct {
    	ElemType data[MaxSize];
    	int length;
    }SqList;
    
  • 顺序存储插入元素平均需要移动的次数(n/2)

  • 顺序存储删除元素平均需要移动的次数(n-1)/2

  • 链表数据结构

    typedef struct LNode{
    	ElemType data;
    	struct LNode *next;
    }LinkNode;
    

3.广义表

  • 举例:
    A = ()  //空表
    B = (e) //长度为1的表
    C = (a,(b,c,d)) //长度为2的表 包含一个原子(整型、实型、字符型)和一个子表
    D = (A,B,C) = ((),(e),(a,(b,c,d))) //长度为3的表
    
    操作
    注意:A 没有表头表尾无法进行Head 和Tail操作
    Head(B) = e
    tail(B) = ()
    head(C) = a
    tail(C) = ((b,c,d))
    head(D) = ()
    tail(D) = ((e),(a,(b,c,d)))
    
    

4.栈与队列

  • 栈(先进后出)、队列(先进先出)

  • 栈的应用(括号匹配、进制转换、表达式求值、递归)、队列的应用(缓冲区、图的层次遍历)

  • 循环队列判空、判满的条件

    判空:Q->rear == Q->front 或 Q.rear == Q.front

    判满:Q->front->next == Q->rear 或( Q.rear + 1 ) % maxSize ==Q.front

  • 栈判空判满的条件

    判空: S.top == -1

    判满:S.top == MaxSize-1

5.数组和特殊矩阵

  • 一维数组ai地址公式:LOC(ai) = LOC(a1)+(i-1)*k (2<=i<=n) k为每个元素占用的存储单元

  • 稀疏矩阵的存储方式:十字链表、三元组

  • 设数组为A[m][n](m 行 n 列),要求的数组为A[i][j](i 行 j 列),L为存储单元所占空间。(0,0)开始

    ​ 行优先:

    ​ LOC(i,j) = LOC(0,0) + [i * n + j] *L;

    ​ 列优先:

    ​ LOC(i,j) = LOC(0,0) + [j * m + i] *L;

  • 二维数组的行优先,列优先转换。
    二维数组A的每个元素是由6个字符组成的串,其行下标=0-8,列下标为=1-10。设每个字符占一个字节,若
    按行先存储,元素A[8,5]的起始地址与A按列存储时起始地址相同的元素是
    A.A[8,5] B.A[3,10] C.A[5,8] D.A[0,9]

  • 解析:
    A[8,5]的偏移量为 8*10+5-1=84
    84/9=9余3,故对应的按列存储地址为A[3,9]
    因为当前列下表从1开始,故为A[3,10]

6.串

7.树和二叉树

①树中的节点数等于所有节点度数之和加一

②度为M的树中第i层上最多有mi-1个节点

③高度为h的M叉树最多有(mh)/(m-1)个节点

④具有n个节点的m叉树最小高度为 ⌈logm(n(m-1))⌉

推导过程:由③,(mh)/(m-1),知道 节点数 n大于h-1层的节点数,小于等于h层满节点(mi-1

​ (mh-1)/(m-1) < n ≤ (mh)/(m-1)

​ mh-1 < n(m-1) v ≤ mh

​ h-1 < logm(n(m-1)) ≤ h

​ hmin = ⌈ logm(n(m-1)) ⌉

⑤高度为H,度为M的树至少有H+M-1个节点

7.图

8.查找

9.排序

排序算法 时间复杂度 空间复杂度 稳定性
最好 平均 最坏
直接插入排序 O(n) O(n2) O(n2) O(1)
冒泡排序 O(n) O(n2) O(n2) O(1)
选择排序 O(n2) O(n2) O(n2) O(1) ×
希尔排序 - - - O(1) ×
快速排序 O(nlog2n) O(nlog2n) O(n2) O(log2n) ×
堆排序 O(nlog2n) O(nlog2n) O(nlog2n) O(1) ×
2路归并排序 O(nlog2n) O(nlog2n) O(nlog2n) O(n)
基数排序 O(d(n+r)) O(d(n+r)) O(d(n+r)) O(r)
811_1 811_2 811_3 811_4 811_5 811_6 811_7 811_8 811_9 811_10 811_11 811_12 811_13 811_14 811_15 811_16 811_17 811_18

二、简答题

1.清晰描述算法的几个基本特征

有穷性:一个算法须在执行有穷步之后结束,且每一步需在有限的时间内完成

确定性:算法的每条指令必须有确定的含义,读者理解时不会产生二义性,即相同的输入只能得到相同的输出

可行性:算法的每一步都是可行的,即每一步都能通过执行有限的次数完成

有输入:一个算法有0个或多个输入

有输出:一个算法有1个或多个输出

2.算法的设计目标

正确性:算法能够正确的执行预先规定的功能和性能要求。(最重要也是最基本的标准)

可使用性:要求算法能够很方便的使用。(用户友好性)

健壮性:要求算法有很好的容错性,即提供异常处理,对不合理的数据进行检查

可读性:算法应该易于使人理解

高效率与低存储量需求:算法效率指算法执行时间,执行时间越短效率越高,存储量是指算法执行过程中所需的最大空间。二者都与问题规模有关

3.简要描述评价查找方法性能的指标与计算公式

平均查找长度ASL是评价算法性能好坏的重要指标

一个查找算法的ASL越大,性能越差,反之ASL越小,性能越好

ASL = ΣPi·Ci

4.算法与程序的区别联系

联系:①算法是解决问题的步骤,程序是算法的代码实现。算法是手段,程序是结果。算法要依靠程序来完成功能,程序需要算法作为灵魂。

​ ②算法+数据结构=程序。算法是程序设计的核心,算法的好坏很大程序上决定一个程序的效率。好的算法可以降低程序运行的时间复杂度和空间复杂度。

区别:①两者的定义不同。算法是对特定问题求解的步骤,是指令的有限序列。程序是实现预期目的而进行操作的一系列语句和指令。

​ ②在语言描述上,程序必须是用规定的程序设计语言来写,算法比较随意

5.数据结构和数据类型的区别和联系

数据结构:相互之间存在一种或多种特定关系的数据元素的集合

​ ①逻辑结构:线性结构、集合、树形结构、图形结构

​ ②存储结构:顺序存储、链式存储、索引存储、散列存储

​ ③数据运算:运算定义(针对逻辑结构,指出运算的功能)

​ 运算实现(针对存储结构,指出具体运算步骤)

数据类型:一个值的集合和定义在此集合的一组操作的总称

​ ①结构类型:“值的集合”为一种数据结构(如线性表、树、图),以及改集合上的一组操作

​ ②原子类型:“值的集合”为一种原子类型(如 Int、double、char),以及该集合上的一组操作

6.逻辑结构和存储结构的关系

逻辑结构:从逻辑上描述数据,与数据的存储无关,是独立于计算机系统的。通常一种逻辑结构可以有多种数据结构,如:线性结构可以采用顺序存储结构或链式存储结构表示

存储结构:是数据对象在计算机中的存储表示。存储各数据元素的数据、数据元素之间的逻辑关系。

7.举例说明相同的逻辑结构,同一种运算在不同存储方式下,其运算效率不同

①线性表采用顺序存储结构,进行插入和删除操作时,需要移动过大量数据元素,时间复杂度为O(n)

②线性表采用链式存储结构,进行插入和删除操作时,只需要修改指针,时间复杂度为O(1)

8.举例说明相同的逻辑结构,相同的存储结构,不同的运算具有不同的特性,是两个不同的数据结构

顺序表和顺序栈

逻辑结构都是线性结构,存储结构都是顺序存储。但是顺序表可以在任意位置进行增删查改,顺序栈只允许在栈顶插入和删除操作,因而栈拥有了先入后出的特性

9.时间复杂度的定义

算法的时间复杂度又称为渐进时间复杂度,它表示随着问题规模n的增大,算法执行时间的增长率和f(n)【渐进上界】的增长率相同

10.数据与数据元素的关系区别

凡是 能被计算机存储、加工的对象统称为数据,数据是一个集合

数据元素是数据的基本单位,是数据的个体

数据元素与数据之间的关系是元素与集合之间的关系

11.简述数据结构中运算描述与运算实现的异同

运算描述是指逻辑结构施加的操作,而运算实现是指一个完成该运算功能的算法

相同:都能完成对数据的“处理”或某种特定的操作

不同:运算描述只是描述处理功能,不包括处理步骤和方法,二运算实现的核心是设计处理步骤

12.数据结构、数据类型和抽象数据类型之间的关系和区别

数据结构:是相互之间存在一种或多种特定关系的“数据元素的集合”

数据类型是指一个值的集合和定义在此集合的一组操作的总称

抽象数据类型是指用户进行软件系统设计时从问题的数学模型中抽象出来的逻辑数据结构和逻辑数据结构上的运算,而不考虑计算机的具体存储结构和运算的具体实现算法。

13.引用运算符在算法描述中的主要作用

在算法设计中,一个算法通常用一个或多个C/C++函数实现,在函数之间传递参数时有两种情况

①实参到形参的单项值传递②实参和形参的双向值传递

在对形参使用引用运算符,即在形参名前加上“&”,不仅可以实现双向值传递,而且使算法设计简单明了

14.简述线性表两种存储结构的特点

顺序存储结构:

①存储密度大:数据元素中只有自身数据

②存储空间利用率低:需要分配一整块较大的存储空间

③随机存取:逻辑上相邻的元素在物理上相邻,通过元素的逻辑序号即可直接存取元素值

④插入删除会引起大量元素移动

链式存储结构:

①存储密度小:数据节点中除了自身数据还有表示逻辑关系的指针域

②存储空间利用率高:无需分配整块存储空间

③不能随机存取:逻辑上相邻的元素物理上不一定连续

④插入删除灵活,不许移动节点,只需要修改指针

15.单链表设置头结点的作用

①便于首元结点的处理:增加头节点以后,首元节点的地址保存在头结点的指针域中,则对链表第一个元素的操作和其他元素相同,无需特殊处理。

②便于空表和非空表的统一处理:增加头节点后,无论链表是否为空,头指针都指向头结点的非空指针

16.在单链表中,能否从当前结点出发访问到任何一个结点

不能,单链表不能访问当前节点之前的节点,只能访问之后的节点

17.比较线性表,栈,队列的异同

相同:都是逻辑结构,都能用顺序存储和链式存储实现

不同:① 线性表:可以在任何位置插入删除

②栈:只能在栈顶插入删除,有先入后出的特点

③队列:只能在队尾插入,队头删除,有先进先出的特点

18.顺序队列假溢出的避免方法及队空队满的判定条件,并求元素个数

避免方法:将顺序队列变成一个环状空间,即循环队列

队满:(rear+1)% MaxSize = front

队空:rear=front

元素个数:(rear+MaxSize-front)%MaxSize

19.在一个算法中需建立多个栈时可以选用三种方案之一,比较优缺点

(1)分别用多个顺序存储空间建立多个独立顺序栈

(2)多个栈共享一个顺序存储空间

(3)分别建立多个独立链

方案一:

​ 优点:每个栈都使用一个顺序存储空间,操作简单

​ 缺点:分配空间小了容易溢出,大了产生浪费,各个栈不能共享空间

方案二:

​ 优点:多个栈共用一个顺序存储空间,充分利用了存储空间,只有整个存储空间都用完了后才会产生溢出

​ 缺点:当一个栈接近满时,要向两端查询是否还有空闲单元,如果有,则要移动元素和修改相关的栈顶与栈底指针。这一计算与移动费时费力

方案三:

​ 优点:多个链栈无需考虑溢出

​ 缺点:需要存储指针,占用更多空间

20.简述栈,队列的定义、特点、基本操作

栈:

​ ①定义:只允许在一端插入和删除操作的线性表

​ ②特点:先进后出

​ ③基本操作:InitStack(&S)初始化栈 StackEmpty(S)判空 Push(&S) 入栈

​ Pop(&S) 出栈 GetTop(S) 获取栈顶元素 DestoryStack(&S)销毁栈

队列:

​ ①定义:只允许一段插入元素,另一端删除元素的线性表

​ ②特点:先进先出

​ ③基本操作:InitQueue(&Q) 初始化队列 QueueEmpty(Q)判空 EnQueue(&Q) 入队

​ DeQueue(&Q) 出队 GetHead(Q) 获取对头元素

21.以下几种存储方式哪个最适合用作链栈

a. 带头结点的单链表. b. 不带头结点的循环单链表. c. 带头结点的双链表.

栈元素之间的逻辑关系属于线性关系,可以采用单链表、循环链表、双链表之一存储,而栈的主要运算是入栈和出栈。

采用a时,前端作为栈顶,入栈和出栈时间复杂度为O(1)

采用b时,前端作为栈顶,入栈和出栈时,首节点都会发生变化,还需要找到尾节点,通过修改next指针使其变为循环单链表,时间复杂度为O(n)

采用c时,前端作为栈顶,进栈出栈时间复杂度都为O(1),但单链表存储密度更高,占用更少的存储地址,因此更适合作为链栈

22.什么是环形队列,怎么实现环形队列

当数组表示队列时,把数组看成环形,即令数组中第一个元素紧跟在最后一个元素之后,就形成了环形队列。环形队列解决了假溢出现象,通常采用逻辑上求余数的方法实现环形队列,假设数组大小为n,当元素下标i增加1时,采用 i = (i+1)%n 实现

23.简述递归算法的优缺点

优点:结构清晰,可读性强,容易用数学归纳法证明其正确性,为设计算法、调试程序带来很大方便

缺点:运行效率低,耗费时间长,占用存储空间多

24.简述递归算法的步骤

①对原问题F(Sn)分析,假设出合理的小问题F(Sn-1)

②假设小问题F(Sn-1)可解,在基础上确定大问题的解,即给出F(Sn)和F(Sn-1)的关系,确定递归主体

③确定特定情况作为递归出口

25.串是一种特殊的线性表,请从存储和运算两方面分析它的特殊之处

存储:串中每个元素是单个字符,在设计串存储结构时可以每个存储单元或者结点只存储一个字符

运算:串有链连接、判断串相等、求子串、和子串替换等基本运算,这是线性表的基本运算中没有的

26.为什么模式匹配中BF算法是有回溯的,而KMP是无回溯算法.

设串为s,模式串为t

BF:当t[i]=s[j]时,置i++,j++

​ 当t[i]!=s[j]时,置i=i-j+1,j=0

​ 若联合和字符不等,目标串指针i会回退,因此,BF算法是有回溯的算法

KMP:当t[i]=s[j]时,i++,j++

​ 当t[i]!=s[j]时,i不变,j=next[j]

​ 当两字符不等时,目标串s的指针i不会回退,只会保持不变,因此KMP算法是没有回溯的算法

27.在KMP算法中计算模式串的next时,若j = o,为什么置next[0]=-1

当模式串t[0]字符与目标串中某字符s[i]不等时,此时置next[0]=-1,表示模式串中没有字符可以与目标串s[i]比较,目标串当前指针i应后移到下个字符,在和模式串t[0]比较

28.简述几种特殊矩阵的压缩思路

对称矩阵:对称矩阵的元素按照主对角线对称,即上三角部分与下三角部分对应相等,因此在存储时,可以只存储上三角元素和对角线元素,或下三角元素和对角线元素,让对称的元素共享同一个存储空间,对称矩阵采用行序为主序,存储主对角线加下三角部分,存储在一维数组B[0...n(n+1)/2-1]中

计算公式为

① k = i(i+1)/2+j (i>=j)(下三角部分)

② k = j(j+1)/2+i (i<j)(上三角部分)

上三角矩阵:采用行序为主序存储主对角线加上三角元素,下三角部分存储为常数c,并将压缩结果存储到一维数组B[0....n(n+1)/2]中

计算公式

① k = i(2n-i+1)/2+j-i (i<=j) (上三角部分)

② k = n(n+1)/2 (i>j)(下三角元素)

下三角矩阵:采用行序为主序存储主对角线加下三角元素,上三角部分存储为常数c,并将压缩结果存储到一维数组B[0....n(n+1)/2]中

计算公式

① k = i(i+1)/2+j (i>=j) (上三角部分)

② k = n(n+1)/2 (i<j)(下三角元素)

29.对于稠密图和稀疏图,采用邻接矩阵和邻接表哪个更好

邻接矩阵适合边稠密图,因为占用的存储空间和边的个数无关

邻接表适合边稀疏图,因为占用的存储空间和边的个数有关

30.阐述图与树中深度优先和宽度优先搜索的原理

深度优先搜索:类似于树的先序遍历。

​ ①访问起始顶点V,从V出发

​ ②访问与V临接的未被访问的任一顶点W1

​ ③再访问与W1临接的未被访问的任一顶点W2,重复此操作,直到不能向下访问退回最近被访问的顶点

​ ④若有未被访问过的顶点,重复执行上述操作,知道所有顶点都被访问完毕

宽度优先搜索:类似树的层序遍历

​ ①访问起始顶点V,从V出发

​ ②依次访问与顶点V相邻的没有被访问过的所有顶点,W1,W2,......,Wn

​ ③再依次访问与顶点W1,W2,...,Wn相邻的未被访问的顶点

​ ④再从刚被访问的顶点出发,重复执行到全部顶点访问完毕

31.树的存储方法有哪些,画出树并举例.

树的存储方法有:① 双亲表示法 ② 孩子表示法 ③ 孩子兄弟表示法

微信图片_20231202232220

32.简述归并排序的思路

归并排序是多次将两个或两个以上的有序表合并成一个新的有序表。最简单的归并时直接将两个有序的子表合并成一个有序的表,即二路归并。二路归并的基本思路是

​ ①待排序表有n个记录,看作n个长度为1的有序序列,两两归并

​ ②得到[n/2]个长度为2或1的有序表,再两两归并

​ ③重复,直到合并成为一个长度为N的有序表为止

33.与竞赛排序相比,堆排的优点

在两者的时间复杂度属于同一量级时,堆排序只需要一个记录大小供交换使用的辅助空间,在调整时,子女只和双亲比较,避免了过多存储空间及和与最大值比较

34.外排序中两个独立阶段

①产生初始归并段 ②多路归并排序

35.详细说明顺序查找、折半查找的ASL成功与ASL失败的计算。

ASL:平均查找长度,即一次查找需要的次数,评价查找算法性能的指标。
查找成功的ASL:每个元素被查找的概率*查找该元素的比较次数的总和。

查找失败的ASL:根据已有的元素将失败情况划分为多个部分,计算各部分被查找的概率*确认其失败所需要的比较次数的总和。

image-20231205225458066
36.B树与B +树的区别

image-20231205231126821

①B树中有n棵子树的节点含有n+1个关键字,B+树中有几颗子树的结点含有几个关键字

②B+树中所有的叶子节点中包含了全部关键码的信息及指向含有这些关键字记录的指针,且叶子节点本身依关键字的大小自小向大的顺序链接

③B+树的非终结结点中仅含其子树根节点中最大(小)关键字,非叶结点具有索引作用

37.直插、希尔、冒泡、快排、选择、堆排、归并(32题)、基排的原理
38.已知二叉树前序和中序序列,能否构造唯一的二叉树?已知前序和后序序列,能否构造出唯一二叉树,为什么?

给定二叉树结点的前序序列和中序序列,可以唯一确定该二叉树。因为前序序列的第一个元素是根结点,该元素将二叉树中序序列分成两部分,左边(设l个元素)表示左子树,若左边无元素,则说明左子树为空;右边(设r个元素)是右子树,若为空,则右子树为空。根据前序遍历中“根—左子树—右子树”的顺序,则由从第二元素开始的l个结点序列和中序序列根左边的l个结点序列构造左子树,由前序序列最后r个元素序列与中序序列根右边的r个元素序列构造右子树。

由二叉树的前序序列和后序序列不能唯一确定一棵二叉树。因为无法确定左右子树两部分。例如,任何结点只有左子树的二叉树和任何结点只有右子树的二叉树,其前序序列相同,后序序列相同,但却是两棵不同的二叉树。

三、应用题

1.画出哈夫曼树并求带权路径长度

通信的电文由字符集{a,b,c,d,e,f,g}中的字符组成,他们在电文中出现的频率分别为{0.31,0.16,0.1,0.08,0.11,0.20,0.04}

1)画出哈夫曼树的构造过程

2)为7个字符设计哈夫曼编码

3)求此哈夫曼树的路径长度和带权路径长度

4)求出使用哈夫曼编码比使用登场编码使电文总长压缩多少

2.求KMP算法的next数组与nextval数组

例题1 给出字符串'a b a c a b a a a d'在KMP算法中的next和nextval数组

a b a c a b a a a d
1 2 3 4 5 6 7 8 9 10
next[ ] 0 1 1 2 1 2 3 4 2 2
nextval[] 0 1 0 2 0 1 0 4 2 2

例题2 主串S='abbacbabbcabbcabbcabcaabbc',子串'abbcabcaa' ,若使用简单模式匹配算法查找成功需要比较多少次?查找成功的平均查找长度为多少?若使用KMP算法查找成功需要比较多少次?查找成功的平均查找长度为多少?计算出相应的next[ ]数组和nextval[ ]数组

简单模式匹配算法:

a b b a c b a b b c a b b c a b b c a b c a a b b c
A B B C A B C A A
4 1 1 2 1 1 7 1 1 1 7 1 1 1 9

简单模式匹配算法比较次数等于4+1+1+2+1+1+7+1+1+1+7+1+1+1+9=39次

KMP算法

子串的next 与nextval 数组

A B B C A B C A A
0 1 2 3 4 5 6 7 8 9
next 0 1 1 1 1 2 3 1 2
nextval 0 1 1 1 0 1 3 0 2

子串与主串的比较

a b b a c b a b b c a b b c a b b c a b c a a b b c
A B B C A B C A A
4 2 1 1 7 5 7

KMP模式匹配算法比较次数等于4+2+1+1+7+5+7=27次

3.已知二叉树中序序列和前序(后序)序列,构造二叉树,并做前序(中序、后序)线索化

4.森林转换为二叉树

5.构造二叉排序树,删除某个节点

6.构造平衡二叉树

7.求连通分量、强连通分量

8.图的遍历

9.求图的深度、宽度优先生成树

10.Prim 和Kruskal算法求最小生成树

11.Dijkstra 求单源最短路径

12.Floyd算法求各顶点之间的最短路径

13.画出图的邻接矩阵、邻接表、写出全部拓扑排序,写出所有事件、活动允许发生的最早和最晚发生时间,写出关键路径

14.根据所给序列,给出直接插入排序、希尔排序、冒泡排序、快速排序、选择排序、堆排序、归并排序、基数排序的每一大趟序列

四、代码题

1.删除线性表中元素值为x的元素
//顺序表
void delete(SeqList &L , ElemType x){
    int i,j=0;
    for(i=0;i<L.length;i++){
        if(L.data[i]!=x){ 
            L.data[j++]=L.data[i];//把不相等的元素依次放置到[0,j],相等的直接跳过
        }
    }
    L.length=j;
}

//链表
void delete(LinkList *L,ElemType x){
    LNode *p = L;
    if(p==NULL)return;
    while(p->next!=NULL){
        if(p->next->data==x){
            LNode *q = p->next;
            p->next=q->next;
            free(q);
        }else{
            p=p->next;
        } 
    }
}
2.删除有序线性表中的相同元素
//顺序表
void delete(SeqList &L){
    if(L.length==0)return;
    ElemType pre=L.data[0];
    int i,j=0;
    for(i=1;i<L.length;i++){
        if(L.data[i]!=L.data[j]){
            L.data[++j]=L.data[i];
        }
    }
    L.length = j+1;
}
//链表
void delete(LinkList *L){
    LNode *p = L;
    while(p->next!=NULL){
        if(p->data==p->next->data){
            LNode *q = p->next;
            p->next=q->next;
            free(q);
        }else{
            p=p->next;
        }
    }
}
3.反转线性表
//顺序表方法
void reverse(SeqList &L){
    //思想:从前往后(到n/2为止)交换对应位置上的元素值
    int i,temp;
    for(i=0;i<L.length/2;i++){
        //交换对应位置上的元素
        temp = L.data[i];
        L.data[i] = L.data[L.length-1-i];
        L.data[L.length-1-i] = temp;
    }
}
//链表逆置
void reverse(LinkList *L){
    //从第二个节点开始采用头插法插入
    LNode *p = L->next;
    L->next = NULL;
    while(p){
        LNode *q = p->next;
        p->next = L->next;
        L->next = p;
        p = q;
    }
    
}

4.循环左移线性表n个元素
//顺序表实现 ⭐⭐⭐⭐⭐运用逆置实现
void move(SeqList &L , int n){
    n = n%L.length;//移动length相当于没移动
    reverse(L,0,n);
    reverse(L,n,L.length);
    reverse(L,0,L.length);
}
void reverse(SeqList *L ,int low , int high){
	int i,temp;
	for(i=0;i<(high-low)/2;i++){
		//交换对应位置上的元素
		temp = L.data[low+i];
		L.data[low+i] = L.data[high-1-i];
		L.data[high-1-i] = temp;
	}
}

//链表实现
int getLength(LinkList *L){
    int len = 0;
    LNode *p = L->next;
    while(p!=NULL){
        len++;
        p = p->next;
    }
    return len;
}

void move(LinkList *L , int n){
    //找到第n个元素和最后一个元素,将后面元素断链,然后插入链表头
    int n = n%getLength(L);//循环N的倍数的相当于没动
    LNode *p = L;//第n个节点的直接前驱
    for(int i = 0;i<n;i++) p = p->next;
    //此时指向第N个元素
    LNode *q = p;
    while(q->next!=NULL)q=q->next;
    //此时q指向最后一个元素
    q->next = L->next;
    L->next = p->next;
    p->next = NULL;
}

5.合并两个升序线性表
//顺序表
SeqList merge(SeqList L1 , SeqList L2){
    SeqList *L ;
    initList(L);
    int len1 = 0,len2 = 0 , k = 0;
    while(len1<L1.length||len2<L2.length){
        if(len2>=L2.length||(len1<L1.length&&L1.data[len1]<=L2.data[len2])){
            L.data[k++]=L1.data[len1++];
        }else{
            L.data[k++]=L2.data[len2++];
        }
    }
    L.length = k;
    return L;
}

//链表
void merge(LinkList *L1,LinkList *L2){
    //合并到L1
    LNode *p = L1->next;
    LNode *q = L2->next;
    LNode *s = L1;
    while(p&&q){
        if(p->data<=q->data){
            s->next = p;
            s = p;
            p=p->next;
        }else{
            s->next = q;
            s = q;
            q = q->next;
        }
    }
    if(q)s->next = q;
    else s->next = p;
    
}
6.将所有偶数元素放到奇数元素之前
//顺序表算法,利用快排思想,比较两端的元素的奇偶性然后交换,偶数在前,奇数在后
void preOdd(SeqList &L){
    int l=0,r=L.length-1;
    int temp = L[0];
    while(l<r){
        while(l<r&&L.data[j]%2==1)j--;
        L.data[i]=L.data[j];
        while(l<r&&L.data[i]%2==0)i++;
        L.data[j]=L.data[i];
    }
    L.data[l] = temp;
    
}
//链表算法:利用辅助空间比较简单不再赘述,不利用辅助空间时且复杂度在O(n),记录已扫描过的最后一个偶数元素,当扫描到偶数时,插入到该元素之后,然后更新记录
void preOdd(LinkList *L){
    LNode *even = L;//记录最后一个偶数
    LNode *p=L;
    while(p->next){
        if(p->next->data%2==0){//偶数需要摘出来再插入到even之后
            LNode *q = p;
           //避免断链
            q->next = p->next->next;
            //将p指向后面的偶数的元素,然后插入到even之后
            p = p->next;
            p->next = even->next;
            even->next = p;
            even = p;
            //还原指针
            p = q;
        }//奇数不用管
    }
}
7.查找线性表第K小的元素
//在l,h区间内查找
int kth(SeqList L,int k, int l,int h){
    if(l>h)return -1;
    int i=l,j=h;
    int temp = L.data[i];
    while(i<j){
        while(i<j&&L.data[j]>=temp)j--;
        L.data[i]=L.data[j];
        while(i<j&&L.data[i]<=temp)i++;
        L.data[j]=L.data[i];
    }
    L.data[i]=temp;
    if if(i==k){
        return L.data[i];
    }else if(i<k)return kth(L,k,l,i-1);
    else return kth(L,k,i+1,h);
}
8.二分查找关键字key
//非递归,时间复杂度O(logn),空间复杂度O(1)
int BiSearch(SeqList L,ElemType key){
    int low = 0,high = L.length-1;
    while(low<=high){
        int mid = (low+high)/2;
        if(key==L.data[mid])return mid;
        else if(key<L.data[mid])high = mid-1;
        else low = mid+1;
    }
    
}
//递归,时间复杂度O(logn),空间复杂度O(logn)
int BiSearch(SeqList L,ElemType key,int low ,int high){
    if(low>high)return -1;
    int mid = (low+high)/2;
    if(key==L.data[mid])return mid;
    else if(key<L.data[mid])return BiSearch(L,ket=y,low,mid-1);
    else return BiSearch(L,key,mid+1,high);
}
9.查找有序顺序表中第一个元素值为X的下标
int BiSearchFirst(SeqList L,ElemType x){
    int low=0,high=L.length-1,mid;
    while(low<=high){
        mid=(low+high)/2;
        if(key<=a[mid]){ //小于等于都往左边查找
            high = mid-1;
        }else{
            low = mid+1;
        }
    }
    return L.data[low]==key?low:-1
}
10.查找两个等长有序表的中位数(二分)
ElemType midNum(SeqList L1,SeqList L2){
    int s1 = 0 , e1 = L1.length-1 , s2 = 0,e2=L2.length-1,m1,m2;
    while(s1!=e1 || s2!=e2){//表中还有元素
        m1=(s1+e1)/2;//两个表中的中位数
        m2=(s2+e2)/2;
        if(L1.data[m1] == L2.data[m2]){
            return L1.data[m1];
        }else if(L.data[m1] < L2.data[m2]){
            s1 = m1 + (e1-s1)%2;//舍弃表1的前半部分,由于需要保证两个序列舍弃的元素相等,m1向下取整
            s2 = m2;
        }else{
            s1 = m1;
            s2 = m2 + (e2-s2)%2;
        }
        
    }
    return L1.data[s1]<L2.data[s2]?L1.data[s1]:L2.data[s2];
}
11.找出线性表中出现次数大于一半的元素
//原理:设一个变量保存当前值。设一个次数,当前值与下一个值进行比較。假设相等,次数加一,假设不相等。次数减一。假设次数减到0了还是不相等,就把当前值替换掉。
ElemType major(SeqList L){
    ElemType m = L.data[0];
    int cnt=1;//最多元素出现次数减去其他元素出现次数
    for(int i=1;i<L.length;i++){
        if(L.data[i]!=m){
            cnt--;
            if(cnt==0){//如果为了0,则m不可能是出现次数大于一半的数
                m = L.data[i];
                cnt=1;
            }
        }else{
            cnt++;
        }
    }
    cnt=0;
    fot(int i=0;i<L.length;i++){//统计m在L中出现的次数
        if(L.data[i]==m)cnt++;
    }
    if(cnt>=(L.length+1)/2)return m;//超过一半,返回m
    else return -1;
}
12.找出有共同后缀的两条单链表中第一个公共节点
LinkList searchFirstCommon(LinkList L1,LinkList L2){
    int len1 = L1.length,len2=L2.length;
    LNode *p = L1->next,*q=L2->next;
    while(len1!=len2){//先统一长度
        if(len1>len2){
            p=p->next;len1--;
        }else{
            q=q->next;len2--;
        }
	}
    while(p!=NULL&&p!=q){//再比较正误
        p=p->next;
        q=q->next;
    }
        
    return p;
}
13.判断链表是否有环
bool isExistLoop(LinkList L){
    LNode *fast,*low;
    fast = low = L.head;
    if(L.head==NULL||head->next==NULL||head->next->next==NULL)return false;
    while(fast!=NULL&&low!=NULL&&fast->next!=NULL){
        fast=fast->next->next;//快指针一次走两步,慢指针一次走一步
        low=low->next;
        if(fast==low)return true;//如果碰头则有环
    }
    return false;
}
14.十进制转为任意进制
void constrasion(int num , int hex){
    //num 为十进制
    if(num<0){
    	num = -num;
        printf("-");
    }
    if(num==0){
        printf("0");
        return 0;
    }
    stack S;
    initStack(S);
    while(num){
        Push(S,num%hex);
        num /= hex;
    }
    while(!stackEmpty(S)){
        if(getTop(S)<10)printf("%d",getTop(S));
        else printf("%c",getTop(S)-10+'A');
       	Pop(S);
    }
    
}
15.字符串回文串判定
16.迷宫求解
17.斐波那契数列
18.汉诺塔
19.中缀转后缀
20.括号匹配判断
21.二叉树链式存储的定义
22.二叉树先序遍历(递归、非递归)
23.二叉树中序遍历(递归、非递归)
24.二叉树后序遍历(递归、非递归)
25.二叉树层次遍历
26. 统计二叉树叶子节点、单支结点、双支结点的数量
27.统计二叉树中节点的最大值、最小值
28.求二叉树高度(递归、非递归)
29.求二叉树宽度(递归、非递归)
30.求二叉树第k层节点数量
31.求x结点所在层次
32.判断二叉树是否为完全二叉树
33.判断两个二叉树是否为镜像
34.判断是否为二叉排序树
35.判断二叉排序树是否为平衡二叉树
36.交换二叉树左右孩子
37.删除二叉树,并释放空间
38.删除以节点值x为根的子树
39.查找二叉树结点p和q的最近的公共祖先
posted @ 2023-11-25 22:40  啊好没吃呢  阅读(35)  评论(0编辑  收藏  举报