数据结构 王道2026学习笔记

第一章 绪论

1.1 数据结构的基本概念

概念和术语

  • 数据数据是描述客观事物的符号,是计算机中可以操作的对象,是能被计算机识别,并输入给计算机处理的符号集合。

  • 数据元素数据元素是数据的基本单位,在计算机中通常作为一个整体进行考虑和处理。

  • 数据项一个数据元素可以由若干个数据项组成,数据项是数据不可分割的最小单位。

  • 数据对象数据对象是性质相同的数据元素的集合,是数据的子集。

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

    • 原子类型:其值不可再分的类型,如整型、浮点型、字符型等。
    • 结构类型:其值可以再分的类型,如整形数组、浮点型数组、结构体、链表、栈、队列等。
    • 抽象数据类型(Abstract Data Type, ADT):抽象数据类型是指一个数学模型及定义在该模型上的一组操作。

数据结构三要素

1.逻辑结构:数据元素之间的逻辑关系。

2.存储结构:数据元素及其关系在计算机存储器中的表示。

3.数据运算:针对某种逻辑结构在相应存储结构上的操作。

逻辑结构

alt text

alt text

  • 集合:集合结构中的数据元素除了同属于一个集合外,它们之间没有其他关系。
  • 线性结构:线性结构中的数据元素之间存在一对一的关系。
  • 树形结构:树形结构中的数据元素之间存在一对多的层次关系。
  • 图状结构或网状结构:图状结构中的数据元https://xiaolincoding.com/素之间存在多对多的关系。

存储结构

  • 顺序存储: 用一组连续的存储单元依次存储数据元素,数据元素之间的逻辑关系由元素的存储位置来表示。
  • 链式存储: 用一组任意的存储单元存储数据元素,数据元素之间的逻辑关系用指针来表示。
  • 索引存储: 在存储数据元素的同时,还建立附加的索引表,索引表中的每项称为索引项,索引项的一般形式是(关键字,地址)。
  • 散列存储: 散列存储是根据元素的关键字直接计算出该元素的存储地址,因此散列存储不需要建立索引表。

数据运算

  • 包括运算的定义和运算的实现两个部分。
  • 数据运算的定义是针对逻辑的,指出运算的功能
  • 数据运算的实现是针对存储的,指出运算的具体实现方法

1.2 算法

算法的概念

算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。

算法具有以下五个特性(了解):

  • 有穷性:一个算法必须总在执行有穷步之后结束,且每一步都可在有穷时间内完成。
  • 确定性:算法中每条指令必须有确切的含义,对于相同的输入只能得到相同的输出。
  • 可行性:算法的每一步操作都是可行的,即每一步都能够通过执行有限次数完成。
  • 输入:一个算法有零个或多个输入,这些输入取自于某个特定的对象的集合。
  • 输出:一个算法有一个或多个输出,这些输出是与输入有着某种特定关系的量。

算法达到的目标(了解):

  • 正确性:算法对于合法的输入,能够产生满足要求的输出,对非法输入能够做出相应处理。
  • 可读性:算法应具有良好的可读性,以帮助人们理解。
  • 健壮性:当输入数据不合法时,算法也能做出相关处理,而不是产生异常或莫名其妙的结果。
  • 高效性:算法应尽量简短,算法的执行时间应尽可能短,算法所需的存储空间应尽量小。

算法效率的度量!!!

时间复杂度:算法的时间复杂度是指算法执行时间随问题规模n的变化而变化的函数,记作:T(n)=O(f(n))。其中f(n)是问题规模n的某个函数,O是数量级符号。

空间复杂度:算法的空间复杂度是指算法在计算机内执行时所需存储空间的度量,记作:S(n)=O(g(n))。其中g(n)是问题规模n的某个函数,O是数量级符号。

第二章 线性表

2.1 线性表的定义和基本操作

线性表的定义

线性表是具有相同数据类型的n个数据元素的有限序列,其中n(0≤n≤∞)为线性表的长度,当n=0时,线性表是一个空表。a1是表头元素,an是表尾元素。

线性表的特点:

  • 表中元素个数有限。
  • 表中元素具有逻辑上的顺序性,表中元素有其先后次序。
  • 表中元素具有相同的数据类型,每个元素占有相同的存储空间。
  • 表中元素具有抽象性,即仅讨论元素间的逻辑关系,而不考虑元素本身的特性。

线性表的基本操作

  • InitList(&L):初始化表。构造一个空的线性表L,分配内存空间。
  • Length(L):求表长。返回线性表L中数据元素的个数。
  • LocateElem(L,e):按值查找操作。在表L中查找具有给定关键字值的元素。
  • GetElem(L,i):按位查找操作。获取表L中第i个位置的元素的值。
  • ListInsert(&L,i,e):插入操作。在表L中的第i个位置插入指定元素e。
  • ListDelete(&L,i,&e):删除操作。删除表L中第i个位置的元素,并用e返回其值。
  • PrintList(L):输出操作。按前后顺序输出线性表L的所有元素值。
  • Empty(L):判空操作。若L为空表,则返回true,否则返回false。
  • DestroyList(&L):销毁操作。销毁线性表,释放线性表L所占用的内存空间。

2.2 线性表的顺序表示和实现

顺序表的定义

线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的数据元素,这种存储方式称为顺序存储或顺序表。

alt text

顺序表上基本操作的实现

#include<iostream>
#define MaxSize 100
using namespace std;

enum error_code {success,overflow,underflow}; //成功,上溢出,下溢出

template<typename T>
class SqList{
private:
int count;
T data[MaxSize];
public:
//初始化构造函数
SqList(){
count = 0; //初始长度为0
}

//插入函数(O(n))
error_code listInsert(int i,T x){
if(i<0||i>count){ //判断i位置是否合法
return overflow;
}
if (count==MaxSize){ //判断是否上溢出
return overflow;
}
for (int j=count; j>i; j--){
data[j] = data[j-1]; //将i位置及之后的数据后移一位
}
data[i] = x; //将x插入到i位置
count++;
return success;
}

//删除函数(O(n))
error_code listDelete(int i,T &x){
if(i<0||i>=count){ //判断i位置是否合法(这里顺便判空了)
return underflow;
}
x = data[i]; //将i位置的数据赋值给x
for (int j=i;j <count-1;j++){
data[j] = data[j+1]; //将i位置之后的数据前移一位
}
count--;
}

//查找函数 (O(n))
int listSearch(T x){
for(int i=0;i<count;i++){
if(data[i]==x){
return i;
}
}
return -1; //处理未找到的情况
}

//获取长度函数
int listLength(){
return count;
}

//判空
bool listEmpty(){
return count==0;
}

//打印函数
void listPrint(){
for(int i=0;i<count;i++){
cout<<data[i]<<" ";
}
cout<<endl;
}

// 获取指定位置的元素
T getElem(int i){
return data[i];
}
};

2.3 线性表的链式表示和实现

单链表的定义

单链表是一种线性表的链式存储结构。它由一系列结点(链表中的数据元素)组成,每个结点都由数据域和指针域两部分组成。

单链表的实现

#include<iostream>
using namespace std;

enum error_code {success,overflow,underflow}; //成功,上溢出,下溢出

template<typename T>
struct Node{
T data;
Node<T> * next;
};


template<typename T>
class LinkList{
private:
Node<T> * head;
int listSize;
public:
LinkList(){ //初始化头指针为空
head = new Node<T>; // 创建dummy节点
head->next = nullptr;
listSize = 0;
}

//求表长(O(n) O(1))
int length(){
// int len=0;
// Node<T> * temp = head;
// while(temp!=nullptr){
// len++;
// temp = temp->next;
// }
// return len;
return listSize;
}

//按序号查找
// 获取链表中第i个元素
Node<T> * getElem(int i){
// 如果i小于0或者大于等于链表长度,则返回空指针
if(i<0||i>listSize-1){
return nullptr;
}
Node<T> * temp = head->next;
for(int j=0;j<i;j++){
temp = temp->next;
}
return temp;
}

//按值查找
Node<T> * locateElem(T e){
Node<T> * temp = head->next;
while(temp!=nullptr&&temp->data!=e){
temp = temp->next;
}
return temp;
}

//插入元素
error_code insertElem(int i,T e){
if (i<0||i>listSize){
return overflow;
}
//构造新节点
Node<T> * newNode = new Node<T>;
newNode->data = e;
//找到插入位置的前一个节点
Node<T> * temp = head;
for (int j=0;j<i;j++){
temp = temp->next;
}
newNode->next = temp->next;
temp->next = newNode;
listSize++;
return success;
}

//删除元素
error_code deleteElem(int i){
if (i<0||i>listSize-1){
return underflow;
}
//找到删除前的元素
Node<T> * temp = head;
for (int j=0;j<i;j++){
temp = temp->next;
}
Node<T> * delNode = temp->next;
temp->next = delNode->next;
delete delNode;
listSize--;
return success;
}

//打印
void printList(){
Node<T> * temp = head->next;
while(temp!=nullptr){
cout<<temp->data<<" ";
temp = temp->next;
}
cout<<endl;
}

~LinkList() { // 添加析构函数防止内存泄漏
Node<T>* temp;
while (head != nullptr) {
temp = head;
head = head->next;
delete temp;
}
}
};

int main(){
LinkList<int> list;
list.insertElem(0, 1);
list.insertElem(1, 2);
list.insertElem(2, 3);
list.insertElem(3, 4);
list.insertElem(4, 5);
list.printList(); // 输出:1 2 3 4 5
list.deleteElem(2);
list.printList(); // 输出:1 2 4 5
cout << list.length() << endl; // 输出:4
Node<int>* node = list.getElem(2);
if (node != nullptr) cout << node->data << endl; // 输出:4
Node<int>* target = list.locateElem(3);
if (target != nullptr) cout << target->data << endl; // 不会输出,因为3已被删除

return 0;
}

双链表

单链表如果要访问某个节点的前驱节点,需要从头节点开始遍历,时间复杂度为O(n)。双链表则可以在O(1)时间内访问到前驱节点。

双链表的实现

#include<iostream>
using namespace std;

enum error_code {success,overflow,underflow}; //成功,上溢出,下溢出

template<typename T>
struct Node{
T data;
Node<T> * next;
Node<T> * prev;
};


template<typename T>
class LinkList{
private:
Node<T> * head;
int listSize;
public:
LinkList(){ //初始化头指针为空
head = new Node<T>;
head->next = nullptr;
head->prev = nullptr;
listSize = 0;
}

//求表长(O(n) O(1))
int length(){
return listSize;
}

//按序号查找
// 获取链表中第i个元素
Node<T> * getElem(int i){
// 如果i小于0或者大于等于链表长度,则返回空指针
if(i<0||i>listSize-1){
return nullptr;
}
Node<T> * temp = head->next;
for(int j=0;j<i;j++){
temp = temp->next;
}
return temp;
}

//按值查找
Node<T> * locateElem(T e){
Node<T> * temp = head->next;
while(temp!=nullptr&&temp->data!=e){
temp = temp->next;
}
return temp;
}

//插入元素
error_code insertElem(int i,T e){
if (i<0||i>listSize){
return overflow;
}
//构造新节点
Node<T> * newNode = new Node<T>;
newNode->data = e;
newNode->next = nullptr;
newNode->prev = nullptr;
//找到插入位置的前一个节点
Node<T> * temp = head;
for (int j=0;j<i;j++){
temp = temp->next;
}

newNode->next = temp->next;
if (temp->next != nullptr) {
temp->next->prev = newNode;
}
newNode->prev = temp;
temp->next = newNode;

listSize++;
return success;
}

//删除元素
error_code deleteElem(int i){
if (i<0||i>listSize-1){
return underflow;
}
//找到删除前的元素
Node<T> * temp = head;
for (int j=0;j<i;j++){
temp = temp->next;
}
Node<T> * delNode = temp->next;
temp->next = delNode->next;
if (delNode->next != nullptr) { // 修复空指针问题
delNode->next->prev = temp;
}
delete delNode;
listSize--;
return success;
}

//打印
void printList(){
Node<T> * temp = head->next;
while(temp!=nullptr){
cout<<temp->data<<" ";
temp = temp->next;
}
cout<<endl;
}

~LinkList() { // 添加析构函数防止内存泄漏
Node<T>* temp;
while (head != nullptr) {
temp = head;
head = head->next;
delete temp;
}
}
};

int main(){
LinkList<int> list;
list.insertElem(0, 1);
list.insertElem(1, 2);
list.insertElem(2, 3);
list.insertElem(3, 4);
list.insertElem(4, 5);
list.printList(); // 输出:1 2 3 4 5
list.deleteElem(2);
list.printList(); // 输出:1 2 4 5
cout << list.length() << endl; // 输出:4
Node<int>* node = list.getElem(2);
if (node != nullptr) cout << node->data << endl; // 输出:4
Node<int>* target = list.locateElem(3);
if (target != nullptr) cout << target->data << endl; // 不会输出,因为3已被删除

return 0;
}

循环链表

循环链表是一种特殊的链表,它的最后一个节点的指针指向链表的第一个节点,形成一个环。循环链表可以用来解决一些需要循环遍历的问题,例如约瑟夫环问题。

循环单链表与循环双链表的实现与单链表和双链表类似,只是在链表的末尾添加了一个指向头节点的指针,使得链表形成一个环。

静态链表

静态链表是一种特殊的链表,它的节点存储在数组中,而不是在堆内存中。静态链表的大小在创建时就已经确定,不能动态地增加或减少。

alt text

第三章 栈、队列和数组

栈的定义和基本操作

只允许在一端进行插入或删除的线性表

alt text

当有n个不同的元素入栈的时候,出栈排列有(1/n+1)/C^n_{2n}种

共享栈

一个数组两个栈用,一个用栈底一个用栈顶(好奇怪)

栈的实现(数组实现比较简单,这里采用链栈实现)

链栈的优点在于不存在上溢问题

#include<iostream>
using namespace std;

enum operation_code {success,overflow,underflow};

template <typename T>
struct Node{
T data;
Node<T>* next;
};

template <typename T>
class LinkStack{
private:
Node<T> *top;
int stackSize;

public:
LinkStack(){
top = new Node<T>();
top->next = nullptr;
stackSize = 0;
};

//判空
bool isEmpty(){
return stackSize==0;
}

//入栈
operation_code push(T data){
Node<T>* newNode = new Node<T>();
newNode->data = data;
newNode->next = top->next;
top->next = newNode;
stackSize++;
return success;
}

//读栈顶元素
operation_code getTop(T &x){
if (!top->next){
return underflow;
}
x = top->next->data;
return success;
}

//出栈
operation_code pop(T &x){
if (!top->next){
return underflow;
}
Node<T>* popNode = top->next;
x = popNode->data;
top->next = popNode->next;
delete popNode;
stackSize--;
return success;
}

~LinkStack(){
T temp;
while(!isEmpty()){
pop(temp);
}
delete top;
top = nullptr;
}
};


int main(){
LinkStack<int> stack;

// 测试空栈弹出
int x;
if (stack.pop(x) == underflow) {
cout << "1. 空栈弹出失败测试通过" << endl;
}

// 测试入栈和栈顶
stack.push(10);
stack.getTop(x);
cout << "2. 当前栈顶元素(应为10): " << x << endl;

stack.push(20);
stack.push(30);

// 连续弹出测试
stack.pop(x);
cout << "3. 弹出元素(应为30): " << x << endl;
stack.pop(x);
cout << "4. 弹出元素(应为20): " << x << endl;

// 测试栈顶更新
stack.getTop(x);
cout << "5. 当前栈顶元素(应为10): " << x << endl;

// 清空栈测试
stack.pop(x);
cout << "6. 弹出元素(应为10): " << x << endl;

if (stack.isEmpty()) {
cout << "7. 栈已空测试通过" << endl;
}

// 再次测试空栈操作
if (stack.getTop(x) == underflow) {
cout << "8. 空栈获取栈顶测试通过" << endl;
}

return 0;
}

队列

队列的定义

先进先出的受限线性表

alt text

顺序存储

这里有一个假溢出的概念

alt text

这个时候如果使用rear == front 判断栈满,就会出现假溢出现象

引入循环队列:

alt text

还是无法判断队列满不满(rear == front 可能是空,也可能是满)

  • 牺牲一个存储单元
    • (rear+1) % maxSize == front 满
    • rear == front 空
    • (rear - front + MaxSize) % MaxSize 队内元素
  • 增加size属性
    • size == 0 空
    • size == maxSize 满
  • 增加tag
    • 出队的时候 将 tag = 0, 入队的时候 将 tag = 1
    • tag == 0 && rear == front 空
    • tag == 1 && rear == front 满

循环队列的实现

#include<iostream>
using namespace std;

#define MaxSize 100
enum operation_code {success,overflow,underflow};

template <typename T>
class Queue{
private:
T data[MaxSize];
int size;
int front;
int rear;
public:
//构造初始化
Queue(){
front = 0;
rear = 0;
size = 0;
}
//判空
bool isEmpty(){
return size==0;
}
//判满
bool isFull(){
return size==MaxSize;
}
//入队
operation_code push(T x){
if (isFull()){
return overflow;
}
data[rear]= x;
rear = (rear + 1)%MaxSize;
size++;
return success;
}
//出队
operation_code pop(T &x){
if (isEmpty()){
return underflow;
}
x = data[front];
front = (front + 1)%MaxSize;
size--;
return success;
}
};

int main() {
Queue<int> q;
int x;

// 填满队列
for (int i = 0; i < 100; ++i) {
if (q.push(i) != success) {
cout << "队列已满,无法入队元素 " << i << endl;
}
}

// 测试满队列入队
operation_code result = q.push(100);
cout << "满队列入队结果: " << result << " (1 表示overflow)" << endl;

// 清空队列
while (q.pop(x) == success) {
cout << x << " ";
}
cout << "\n队列已清空" << endl;

// 测试空队列出队
result = q.pop(x);
cout << "空队列出队结果: " << result << " (2 表示underflow)" << endl;

// 正常入队和出队测试
q.push(10);
q.push(20);
q.pop(x);
cout << "出队元素: " << x << endl; // 应输出10
q.pop(x);
cout << "出队元素: " << x << endl; // 应输出20

return 0;
}

链式队列

#include<iostream>
using namespace std;

enum operation_code {success,overflow,underflow};

template <typename T>
struct Node{
T data;
Node<T>* next;
};

template <typename T>
class Queue{
private:
Node<T>* front;
Node<T>* rear;
int size;
public:
Queue(){
front = new Node<T>();
front->next = nullptr;
rear = front;
size = 0;
}

//判空
bool isEmpty(){
// return rear == front;
return size == 0;
}

//入队
operation_code push(T x){
Node<T> * newNode = new Node<T>();
newNode->data = x;
newNode->next = nullptr;
rear->next = newNode;
rear = newNode;
size++;
return success;
}

//出队
operation_code pop(T &x){
if (isEmpty()){
return underflow;
}
Node<T> *tmp = front->next;
x = tmp->data;
front->next = tmp->next;
if (rear == tmp){
rear = front;
}
delete tmp;
size--;
return success;
}

~Queue(){
while (front != nullptr) {
Node<T>* tmp = front;
front = front->next;
delete tmp;
}
}
};

int main(){
Queue<int> q;
int val;

// 测试空队列出队
cout << "空队列出队结果: " << q.pop(val) << " (应返回2 underflow)" << endl;

// 入队测试
q.push(10);
q.push(20);
q.push(30);

// 出队测试
q.pop(val);
cout << "第一次出队元素: " << val << " (应输出10)" << endl;
q.pop(val);
cout << "第二次出队元素: " << val << " (应输出20)" << endl;

// 再次入队
q.push(40);
q.pop(val);
cout << "第三次出队元素: " << val << " (应输出30)" << endl;
q.pop(val);
cout << "第四次出队元素: " << val << " (应输出40)" << endl;

// 测试空队列
cout << "空队列出队结果: " << q.pop(val) << " (应返回2 underflow)" << endl;

return 0;
}

双端队列(了解)

alt text

栈和队列的应用

看一看书,队列的遍历非常重要!!!

alt text

数组(了解)

第五章 树与二叉树

树的定义

树是由n(n≥0)个节点组成的有限集合。当n=0时,称为空树;当n>0时,树由一个根节点和若干棵子树组成。树中的每一个节点都只有一个父节点,除了根节点没有父节点。

任意一个非空树应当满足:

  • 有且仅有一个特定的成为根的节点

  • 当 n > 1 时,其余节点可分为 m(m > 0)个互不相交的有限集合 T1,T2,…,Tm,其中每一个集合 Ti(1 ≤ i ≤ m)本身又是一棵树,并且称为根的子树。

树的这种定义方式称为递归定义。 具有以下特点:

  • 树的根节点没有前驱节点,除根节点外,每个节点有且仅有一个前驱节点

  • 树中所有节点都有零个或多个后继节点

树的基本术语

alt text

  • 祖先、子孙、双亲、孩子、兄弟和堂兄弟
  • 节点的层次、深度和高度
  • 节点的度和树的度
  • 分支节点和叶节点
  • 有序树和无序树
  • 路径和路径长度
  • 森林

树的性质(了解):

  • 树的节点数等于所有节点的度数加1
  • 度为m的树中第i层上至多有m^(i-1)个节点(i≥1)
  • 高度为h的m叉树至多有(m^h-1)/(m-1)个节点
  • 具有n个节点的m叉树的最小高度为logm(n(m-1)+1)
  • 度为m的树中第i层上至多有m^(i-1)个节点(i≥1)

二叉树

二叉树是一种特殊的树,它的每个节点最多有两个子节点,分别称为左子节点和右子节点。二叉树可以是有序的,也可以是无序的。在有序二叉树中,左子树上的节点都小于根节点,右子树上的节点都大于根节点。

二叉树与度为2的树的区别:

  • 度为2的树至少要含有3个节点,而二叉树可以为空
  • 度为2的树种孩子的左右次序是相对的(若是只有一个子树,则左右次序无所谓),而二叉树中孩子的左右次序是绝对的(若是只有一个子树,则也要分左子树或者是右子树)

几种特殊的二叉树:

  • 满二叉树:一棵高度为h,且含有2^h-1个节点的二叉树

    • 对满二叉树的节点进行编号,若从根节点开始,自上而下,自左至右的顺序进行,则对于编号为i的节点,其左孩子编号为2i,右孩子编号为2i+1
  • 完全二叉树:一棵高度为h,且含有n个节点的二叉树,当且仅当其每一个节点都与高度为h的满二叉树中编号为1~n的节点一一对应

  • 二叉排序树:又称二叉搜索树,左子树上所有节点的值均小于它的根节点的值,右子树上所有节点的值均大于它的根节点的值

  • 平衡二叉树:又称AVL树,任何节点的两个子树的高度差不超过1

二叉树的性质::

  • 非空二叉树的叶节点数等于其双分支节点(度为2的节点)的个数加1

  • 非空二叉树中第i层上至多有2^(i-1)个节点

  • 高度为h的二叉树至多有2^h-1个节点(满二叉树)

  • 具有n个节点的完全二叉树的高度为log2(n+1)向上取整

二叉树的存储结构

顺序存储结构

二叉树的顺序存储结构使用数组来存储二叉树的节点,数组的下标表示节点的编号,数组的值表示节点的值。

对于完全二叉树,可以使用顺序存储结构来存储,对于非完全二叉树,可以使用顺序存储结构来存储,但是需要浪费一些空间。

链式存储结构

二叉树的链式存储结构使用链表来存储二叉树的节点,每个节点包含一个数据域和两个指针域,分别指向左子节点和右子节点。

alt text

二叉树的遍历

二叉树的遍历是指按照某种顺序访问二叉树中的所有节点,使得每个节点被访问一次且仅被访问一次。

二叉树的遍历有四种方式:前序遍历、中序遍历、后序遍历和层序遍历。

(二叉树的遍历算法,后面会有代码实现)

  • 前序遍历:先访问根节点,然后递归地访问左子树和右子树

  • 中序遍历:先递归地访问左子树,然后访问根节点,最后递归地访问右子树

  • 后序遍历:先递归地访问左子树和右子树,然后访问根节点

  • 层序遍历:从根节点开始,逐层访问二叉树的每一层,每一层的节点按照从左到右的顺序访问

由遍历构造的二叉树

四种遍历,任意给一种都不能确定,但是只要若已知 中序遍历 和 其他三种遍历的任意一种就可以确定唯一一棵二叉树

#include<iostream>
#include<queue>
using namespace std;

struct treeNode{
char data;
treeNode * left;
treeNode * right;

treeNode(char newData){
data = newData;
left = nullptr;
right = nullptr;
}
};

class BinaryTree{
private:
int size;
treeNode* root;

treeNode * createTree(){
char val;
cin>>val;
if(val=='#'){
return nullptr;
}
treeNode * current = new treeNode(val);
current->left = createTree();
current->right = createTree();
size++;
return current;
}

void preOrder_private(treeNode* T){
if(T!=nullptr){
cout<<T->data<<"->";
preOrder_private(T->left);
preOrder_private(T->right);
}
}

void inOrder_private(treeNode* T){
if(T!=nullptr){
inOrder_private(T->left);
cout<<T->data<<"->";
inOrder_private(T->right);
}
}

void postOrder_private(treeNode* T){
if(T!=nullptr){
postOrder_private(T->left);
cout<<T->data<<"->";
postOrder_private(T->right);
}
}

void levelOrder_private(treeNode* T){
if(T==nullptr){
return;
}
queue<treeNode*> orderQueue;
orderQueue.push(T);
while(!orderQueue.empty()){
treeNode* temp = orderQueue.front();
orderQueue.pop();
cout<<temp->data<<"->";
if(temp->left){
orderQueue.push(temp->left);
}
if(temp->right){
orderQueue.push(temp->right);
}
}
}

int getHeightRecursion_private(treeNode* T){
if(T==nullptr){
return 0;
}
int leftTreeHeight = getHeightRecursion_private(T->left);
int rightTreeHeight = getHeightRecursion_private(T->right);
int resultLength = leftTreeHeight>rightTreeHeight?leftTreeHeight+1:rightTreeHeight+1;
return resultLength;
}

int getHeightLevel_private(treeNode* T){
if(T==nullptr){
return 0;
}
int resultHeight = 0;
queue<treeNode*> orderQueue;
orderQueue.push(T);
while(!orderQueue.empty()){
resultHeight++;
for(int i=0;i<orderQueue.size();i++){
treeNode* temp = orderQueue.front();
orderQueue.pop();
if(temp->left){
orderQueue.push(temp->left);
}
if(temp->right){
orderQueue.push(temp->right);
}
}

}
return resultHeight;
}

int getSize_private(treeNode* T){
if(T==nullptr){
return 0;
}
int leftSize = getSize_private(T->left);
int rightSize = getSize_private(T->right);

return leftSize + rightSize + 1;
}

public:
BinaryTree(){
size = 0;
root = createTree();
}

void preOrder(){
this->preOrder_private(root);
}

void inOrder(){
this->inOrder_private(root);
}

void postOrder(){
this->postOrder_private(root);
}

void levelOrder(){
this->levelOrder_private(root);
}

int getHeightRecursion(){
return this->getHeightRecursion_private(root);
}

int getHeightLevel(){
return this->getHeightLevel_private(root);
}

int getSize(){
return this->getSize_private(root);
}
};

int main(){
cout << "输入二叉树的前序序列(空节点用#表示,例如ABD##E##C#F##):" << endl;
BinaryTree bt;

cout << "前序遍历结果: ";
bt.preOrder();
cout << "END" << endl;

cout << "中序遍历结果: ";
bt.inOrder();
cout << "END" << endl;

cout << "后序遍历结果: ";
bt.postOrder();
cout << "END" << endl;

cout << "层序遍历结果: ";
bt.levelOrder();
cout << "END" << endl;

cout << "递归计算的高度: " << bt.getHeightRecursion() << endl;
cout << "层序计算的高度: " << bt.getHeightLevel() << endl;
cout << "节点总数: " << bt.getSize() << endl;

return 0;
}
// 结果
// ABD##E##C#F##
// 前序遍历结果: A->B->D->E->C->F->END
// 中序遍历结果: D->B->E->A->F->C->END
// 后序遍历结果: D->E->B->F->C->A->END
// 层序遍历结果: A->B->C->D->E->F->END
// 递归计算的高度: 3
// 层序计算的高度: 3
// 节点总数: 6

线索二叉树(看书)

树与森林

树的存储结构

  • 双亲表示法:使用一个一维数组来存储树的节点,每个节点包含一个数据域和一个指向其父节点的指针域。

  • 孩子表示法:使用一个一维数组来存储树的节点,每个节点包含一个数据域和一个指向其孩子节点的指针域。

  • 孩子兄弟表示法:使用一个一维数组来存储树的节点,每个节点包含一个数据域、一个指向其父节点的指针域和一个指向其第一个孩子节点的指针域,以及一个指向其右兄弟节点的指针域。

alt text

alt text

树和二叉树的相互转化

  • 树转化为二叉树:将树中的每个节点都添加一个指向其第一个孩子节点的指针域和一个指向其右兄弟节点的指针域,这样就可以将树转化为二叉树。

  • 二叉树转化为树:将二叉树中的每个节点都删除其指向其右兄弟节点的指针域,这样就可以将二叉树转化为树。

alt text

扩展:

  • 森林转化为二叉树:将森林中的每棵树转化为二叉树,然后将这些二叉树的根节点连接起来,形成一个二叉树。

  • 二叉树转化为森林:将二叉树中的每个节点都删除其指向其右兄弟节点的指针域,这样就可以将二叉树转化为森林。

alt text

树的遍历

  • 先根遍历:先访问根节点,然后依次访问根节点的孩子节点,再依次访问每个孩子节点的子树。
  • 后根遍历:先依次访问根节点的孩子节点,再访问根节点,最后依次访问每个孩子节点的子树。

alt text

森林的遍历

  • 先根遍历:先访问森林中每棵树的根节点,然后依次访问每棵树的根节点的孩子节点,再依次访问每个孩子节点的子树。
  • 中序遍历:先依次访问森林中每棵树的根节点的孩子节点,再访问每棵树的根节点,最后依次访问每个孩子节点的子树。

alt text

alt text

树与二叉树的应用

哈夫曼树

一些概念

  • 路径:从树中一个节点到另一个节点之间的分支构成这两个节点之间的路径。
  • 权重:在树的节点上赋予的有某种意义的数值。
  • 路径长度:路径上的分支数目称为路径长度。
  • 树的带权路径长度:树的路径长度乘上该路径上的权值。
  • 权重最小的路径:带权路径长度最小的路径。

alt text

哈夫曼树的构造:

  • 从给定的节点集合中选出两个权值最小的节点,将它们合并成一个新的节点,新的节点的权值为两个节点的权值之和。
  • 将新的节点重新插入到节点集合中,重复上述步骤,直到节点集合中只剩下一个节点,这个节点就是哈夫曼树的根节点。

alt text

哈夫曼编码:

  • 哈夫曼编码是一种无损压缩编码,它根据字符出现的频率来构造编码,使得出现频率高的字符用较短的编码表示,出现频率低的字符用较长的编码表示。
  • 哈夫曼编码的构造过程与哈夫曼树的构造过程类似,只是将节点的权值替换为字符出现的频率。
  • 哈夫曼编码的解码过程与哈夫曼树的遍历过程类似,只是将遍历过程中遇到的叶子节点替换为对应的字符。

alt text

第六章 图

图的基本概念

图的定义:图是由顶点的有穷非空集合和顶点之间边的集合组成的数据结构,记作G=(V,E),其中V是顶点的集合,E是边的集合。

术语(GPT生成的,看书吧):

  • 有向图:有向图是由顶点的有穷非空集合和顶点之间的有向边组成的图。
  • 无向图:无向图是由顶点的有穷非空集合和顶点之间的无向边组成的图。
  • 带权图:带权图是由顶点的有穷非空集合和顶点之间的带权边组成的图。
  • 简单图:简单图是指没有重复边和重复顶点的图。
  • 多重图:多重图是指有重复边的图。
  • 完全图:完全图是指任意两个顶点之间都有一条边的图。
  • 子图:子图是指由原图的顶点集合和边集合的子集组成的图。
  • 连通图:连通图是指任意两个顶点之间都有一条路径的图。
  • 连通分量:连通分量是指原图的最大连通子图。
  • 入度:入度是指以某个顶点为终点的边的数目。
  • 出度:出度是指以某个顶点为起点的边的数目。
  • 度:度是指某个顶点的入度与出度之和。
  • 路径:路径是指图中顶点序列,其中相邻两个顶点之间有边相连。
  • 环:环是指路径中第一个顶点和最后一个顶点相同,且路径中不重复出现的顶点。
  • 简单路径:简单路径是指路径中不重复出现的顶点。
  • 简单环:简单环是指路径中不重复出现的顶点,且路径中第一个顶点和最后一个顶点相同。
  • 距离:距离是指两个顶点之间的路径长度。
  • 有向无环图:有向无环图是指没有环的有向图。
  • 连通分量:连通分量是指原图的最大连通子图。
  • 强连通图:强连通图是指任意两个顶点之间都有一条有向路径的图。
  • 强连通分量:强连通分量是指原图的最大强连通子图。
  • 生成图:生成图是指由原图的顶点集合和边集合的子集组成的图。
  • 生成树:生成树是指原图的最大连通子图。
  • 最小生成树:最小生成树是指带权图的最小生成树。
  • 最短路径:最短路径是指两个顶点之间的路径长度最小的路径。

图的存储以及基本操作

存储结构主要是邻接矩阵和邻接表,除此之外还有十字链表和邻接多重表。

邻接矩阵

邻接矩阵是一种使用二维数组来表示图的存储结构,其中每个元素表示两个顶点之间是否存在一条边。

alt text

邻接表

邻接表是一种使用链表来表示图的存储结构,其中每个顶点都有一个链表,链表中的每个元素表示与该顶点相邻的顶点。

alt text

#include<iostream>
#include<vector>
using namespace std;

class GraphMatrix{
private:
int nodeNum = 5;
int data[5][5];
public:
GraphMatrix(){
for(int i=0;i<nodeNum;i++){
for(int j=0;j<nodeNum;j++){
data[i][j] = 0;
}
}
data[0][1] = 1; // 0 -> 1
data[0][3] = 1; // 0 -> 3
data[1][4] = 1; // 1 -> 4
data[2][4] = 1; // 2 -> 4
data[3][1] = 1; // 3 -> 1
data[4][3] = 1; // 4 -> 3
}

int firstNeighbor(int x){
if(x>nodeNum-1||x<0){
return -1;
}

for (int i = 0; i < nodeNum; i++){
if(data[x][i]!=0){
return i;
}
}

return -1;

}

int nextNeighbor(int x,int y){
if(x>nodeNum-1||x<0){
return -1;
}
if(y>nodeNum-1||y<0){
return -1;
}

for (int i = y+1; i < nodeNum; i++){
if(data[x][i]!=0){
return i;
}
}
return -1;
}
};

class GraphTable{
private:
const int nodeNum = 5;
vector<int> data[5];
public:
GraphTable(){
data[0].push_back(1); // 0 -> 1
data[0].push_back(3); // 0 -> 3
data[1].push_back(4); // 1 -> 4
data[2].push_back(4); // 2 -> 4
data[3].push_back(1); // 3 -> 1
data[4].push_back(3); // 4 -> 3
}

int firstNeighbor(int x) {
if (x < 0 || x >= nodeNum || data[x].empty()) {
return -1;
}
return data[x][0];
}

int nextNeighbor(int x, int y) {
if (x < 0 || x >= nodeNum || y < 0 || y >= nodeNum) {
return -1;
}
for (int i = 0; i < data[x].size(); ++i) {
if (data[x][i] == y) {
if (i + 1 < data[x].size()) {
return data[x][i + 1];
} else {
return -1;
}
}
}
return -1;
}
};


int main(){
// 测试 GraphMatrix
GraphMatrix gm;
cout << "Testing GraphMatrix:" << endl;
for (int x = 0; x < 5; x++) {
cout << "Node " << x << " neighbors: ";
int current = gm.firstNeighbor(x);
if (current == -1) {
cout << "None" << endl;
continue;
}
cout << current;
while ((current = gm.nextNeighbor(x, current)) != -1) {
cout << ", " << current;
}
cout << endl;
}

// 测试 GraphTable
GraphTable gt;
cout << "\nTesting GraphTable:" << endl;
for (int x = 0; x < 5; x++) {
cout << "Node " << x << " neighbors: ";
int current = gt.firstNeighbor(x);
if (current == -1) {
cout << "None" << endl;
continue;
}
cout << current;
while ((current = gt.nextNeighbor(x, current)) != -1) {
cout << ", " << current;
}
cout << endl;
}

return 0;
}

这个例子有点取巧了,对于临接表法的处理,因为是存储的node是int型的,而且连续的,所以直接使用下标作为顶点表了

图的遍历

广度优先遍历(BFS)和深度优先遍历(DFS)是两种常见的图遍历算法。

#include<iostream>
#include<queue>
using namespace std;

class GraphMatrix{
private:
int nodeNum = 5;
int data[5][5];
bool visited[5];
public:
GraphMatrix(){
for(int i=0;i<nodeNum;i++){
for(int j=0;j<nodeNum;j++){
data[i][j] = 0;
}
}
data[0][1] = 1; // 0 -> 1
data[0][3] = 1; // 0 -> 3
data[1][4] = 1; // 1 -> 4
data[2][4] = 1; // 2 -> 4
data[3][1] = 1; // 3 -> 1
data[4][3] = 1; // 4 -> 3
}

int firstNeighbor(int x){
if(x>nodeNum-1||x<0){
return -1;
}

for (int i = 0; i < nodeNum; i++){
if(data[x][i]!=0){
return i;
}
}

return -1;

}

int nextNeighbor(int x,int y){
if(x>nodeNum-1||x<0||y>nodeNum-1||y<0){
return -1;
}
for (int i = y+1; i < nodeNum; i++){
if(data[x][i]!=0){
return i;
}
}
return -1;
}

//广度优先遍历BFS Traverse
void BFSTraverse(){
//初始化 visited
for(int i=0;i<nodeNum;i++){
visited[i] = false;
};
//遍历 为了防止这个图是非连通图,所以必须这么写
for(int i=0;i<nodeNum;i++){
if (!visited[i]) {
BFS(i);
}

};
}

void BFS(int node){
queue<int> bfsQueue;
bfsQueue.push(node);
cout<<node<<" ";
visited[node] = true;
while(!bfsQueue.empty()){
int temp = bfsQueue.front();
bfsQueue.pop();
for (int i = firstNeighbor(temp); i != -1; i = nextNeighbor(temp,i)) {
if (!visited[i]){
bfsQueue.push(i);
cout<<i<<" ";
visited[i] = true;
}

}

}
}
//深度优先搜索DFS Traverse
void DFSTraverse(){
//初始化 visited
for(int i=0;i<nodeNum;i++){
visited[i] = false;
};
//遍历 为了防止这个图是非连通图,所以必须这么写
for(int i=0;i<nodeNum;i++){
if (!visited[i]) {
DFS(i);
}

};
}

void DFS(int node){
cout<<node<<" ";
visited[node]=true;
for (int i = firstNeighbor(node); i != -1; i = nextNeighbor(node,i)){
if (!visited[i]){
DFS(i);
}

}

}

};


class GraphTable{
private:
const int nodeNum = 5;
vector<int> data[5];
bool visited[5];
public:
GraphTable(){
data[0].push_back(1); // 0 -> 1
data[0].push_back(3); // 0 -> 3
data[1].push_back(4); // 1 -> 4
data[2].push_back(4); // 2 -> 4
data[3].push_back(1); // 3 -> 1
data[4].push_back(3); // 4 -> 3
}

int firstNeighbor(int x) {
if (x < 0 || x >= nodeNum || data[x].empty()) {
return -1;
}
return data[x][0];
}

int nextNeighbor(int x, int y) {
if (x < 0 || x >= nodeNum || y < 0 || y >= nodeNum) {
return -1;
}
for (int i = 0; i < data[x].size(); ++i) {
if (data[x][i] == y) {
if (i + 1 < data[x].size()) {
return data[x][i + 1];
} else {
return -1;
}
}
}
return -1;
}


//广度优先遍历BFS Traverse
void BFSTraverse(){
//初始化 visited
for(int i=0;i<nodeNum;i++){
visited[i] = false;
};
//遍历 为了防止这个图是非连通图,所以必须这么写
for(int i=0;i<nodeNum;i++){
if (!visited[i]) {
BFS(i);
}

};
}

void BFS(int node){
queue<int> bfsQueue;
bfsQueue.push(node);
cout<<node<<" ";
visited[node] = true;
while(!bfsQueue.empty()){
int temp = bfsQueue.front();
bfsQueue.pop();
for (int i = firstNeighbor(temp); i != -1; i = nextNeighbor(temp,i)) {
if (!visited[i]){
bfsQueue.push(i);
cout<<i<<" ";
visited[i] = true;
}

}

}
}
//深度优先搜索DFS Traverse
void DFSTraverse(){
//初始化 visited
for(int i=0;i<nodeNum;i++){
visited[i] = false;
};
//遍历 为了防止这个图是非连通图,所以必须这么写
for(int i=0;i<nodeNum;i++){
if (!visited[i]) {
DFS(i);
}

};
}

void DFS(int node){
cout<<node<<" ";
visited[node]=true;
for (int i = firstNeighbor(node); i != -1; i = nextNeighbor(node,i)){
if (!visited[i]){
DFS(i);
}

}

}
};

int main(){

// 测试 GraphMatrix
GraphMatrix gm;
cout << "Testing GraphMatrix:" << endl;
for (int x = 0; x < 5; x++) {
cout << "Node " << x << " neighbors: ";
int current = gm.firstNeighbor(x);
if (current == -1) {
cout << "None" << endl;
continue;
}
cout << current;
while ((current = gm.nextNeighbor(x, current)) != -1) {
cout << ", " << current;
}
cout << endl;
}

// 测试 GraphMatrix 的 BFS 和 DFS
cout << "\nTesting GraphMatrix BFS and DFS:" << endl;
cout << "BFS starting from 0: ";
gm.BFSTraverse();
cout<<endl;
cout << "DFS starting from 0: ";
gm.DFSTraverse();
cout<<endl;

// 测试 GraphTable
GraphTable gt;
cout << "\nTesting GraphTable:" << endl;
for (int x = 0; x < 5; x++) {
cout << "Node " << x << " neighbors: ";
int current = gt.firstNeighbor(x);
if (current == -1) {
cout << "None" << endl;
continue;
}
cout << current;
while ((current = gt.nextNeighbor(x, current)) != -1) {
cout << ", " << current;
}
cout << endl;
}

// 测试 GraphTable 的 BFS 和 DFS
cout << "\nTesting GraphTable BFS and DFS:" << endl;
cout << "BFS starting from 0: ";
gt.BFSTraverse();
cout<<endl;
cout << "DFS starting from 0: ";
gt.DFSTraverse();
cout<<endl;



return 0;
}

基本思路是这样的,这里要注意,不管是邻接表还是邻接矩阵,只要实现了基础的firstNeighbor与nextNeighbor函数,其流程基本上是差不多的,这个可以多看看,注意理解!!!

最小生成树

prim算法与Kruskal算法

prim算法:从任意一个顶点开始,逐步加入新的顶点,每次都选择权重最小的边,直到所有顶点都被加入为止。

alt text

Kruskal算法:将所有边按照权重从小到大排序,然后依次选择权重最小的边,如果这条边连接的两个顶点不在同一个连通分量中,则将其加入生成树中,直到所有顶点都在同一个连通分量中为止。

alt text

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

class GraphMatrix{
private:
int nodeNum = 6;
int data[6][6];
public:
GraphMatrix(){
for(int i=0;i<nodeNum;i++){
for(int j=0;j<nodeNum;j++){
data[i][j] = 0;
}
}
data[0][1] = 6;data[0][2] = 1;data[0][3] = 5;
data[1][0] = 6;data[1][2] = 5;data[1][4] = 3;
data[2][0] = 1;data[2][1] = 5;data[2][4] = 6;data[2][5] = 4;data[2][3] = 5;
data[3][0] = 5;data[3][2] = 5;data[3][5] = 2;
data[4][1] = 3;data[4][2] = 6;data[4][5] = 6;
data[5][4] = 6;data[5][2] = 4;data[5][3] = 2;
}

int firstNeighbor(int x){
if(x>nodeNum-1||x<0){
return -1;
}

for (int i = 0; i < nodeNum; i++){
if(data[x][i]!=0){
return i;
}
}

return -1;

}

int nextNeighbor(int x,int y){
if(x>nodeNum-1||x<0||y>nodeNum-1||y<0){
return -1;
}
for (int i = y+1; i < nodeNum; i++){
if(data[x][i]!=0){
return i;
}
}
return -1;
}

int getWeight(int x,int y){
if(x>nodeNum-1||x<0||y>nodeNum-1||y<0){
return -1;
}
return data[x][y];
}

};


void prim(GraphMatrix &graph) {
const int n = 6;
int parent[n]; // 存储MST中节点的父节点
int key[n]; // 存储各节点到MST的最小权重
bool mstSet[n]; // 记录节点是否在MST中


// 初始化
for(int i=0; i<n; i++) {
key[i] = 9999;
mstSet[i] = false;
}

key[0] = 0; // 选择节点0作为起始点
parent[0] = -1; // 起始点没有父节点

// 构建MST需要n-1次迭代 有n个点就得有n-1个边
for(int count=0; count < n-1; count++) {
// 找出当前未加入MST且key最小的节点
int u = -1;
int minKey = 9999;
for(int i=0; i<n; i++) {
if(!mstSet[i] && key[i] < minKey) {
minKey = key[i];
u = i;
}
}

if(u == -1) break; // 图不连通,无法形成MST

mstSet[u] = true; // 将节点u加入MST

// 更新u的邻接节点的key值和父节点
for(int v = graph.firstNeighbor(u);v!=-1;v = graph.nextNeighbor(u, v)){
int weight = graph.getWeight(u, v);
if(!mstSet[v] && weight < key[v]) {
parent[v] = u;
key[v] = weight;
}
}
}


// 输出MST
cout << "Prim's MST:" << endl;
int totalWeight = 0;
for(int i=1; i<n; i++) {
int w = graph.getWeight(parent[i], i);
cout << parent[i] << " - " << i << " \tWeight: " << w << endl;
totalWeight += w;
}
cout << "Total Weight: " << totalWeight << endl << endl;
}


// Kruskal算法所需的数据结构
struct Edge {
int u, v, weight;
Edge(int u, int v, int w) : u(u), v(v), weight(w) {}
bool operator<(const Edge &other) const {
return weight < other.weight;
}
};

// 并查集实现
class UnionFind {
private:
vector<int> parent;
public:
UnionFind(int size) {
parent.resize(size);
for(int i=0; i<size; i++) parent[i] = i;
}

int find(int x) {
if(parent[x] != x)
parent[x] = find(parent[x]); // 路径压缩
return parent[x];
}

void unite(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if(rootX != rootY)
parent[rootX] = rootY;
}
};


// Kruskal算法实现
void kruskal(GraphMatrix &graph) {
vector<Edge> edges;
// 收集所有无向边(只保存u < v的情况避免重复)
for(int u=0; u<6; u++) {
int v = graph.firstNeighbor(u);
while(v != -1) {
if(u < v) { // 避免重复添加边
edges.emplace_back(u, v, graph.getWeight(u, v));
}
v = graph.nextNeighbor(u, v);
}
}

sort(edges.begin(), edges.end());

UnionFind uf(6);
vector<Edge> mst;
int totalWeight = 0;

// 遍历所有边构建MST
for(Edge &e : edges) {
if(uf.find(e.u) != uf.find(e.v)) {
uf.unite(e.u, e.v);
mst.push_back(e);
totalWeight += e.weight;
if(mst.size() == 5) break; // MST有n-1条边
}
}

// 输出结果
cout << "Kruskal's MST:" << endl;
for(Edge &e : mst) {
cout << e.u << " - " << e.v << " \tWeight: " << e.weight << endl;
}
cout << "Total Weight: " << totalWeight << endl;
}


int main(){
GraphMatrix graph;
prim(graph);
kruskal(graph);
return 0;
}

最短路径

dijkstra算法和floyd算法

alt text

alt text

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

class GraphMatrix{
private:
int nodeNum = 5;
int data[5][5];
public:
GraphMatrix(){
for(int i=0;i<nodeNum;i++){
for(int j=0;j<nodeNum;j++){
data[i][j] = 0;
}
}
data[0][1] = 10;data[0][4] = 5;
data[1][4] = 2;data[1][2] = 1;
data[2][3] = 4;
data[3][2] = 6;data[3][0] = 7;
data[4][1] = 3;data[4][2] = 9;data[4][3] = 2;
}

int firstNeighbor(int x){
if(x>nodeNum-1||x<0){
return -1;
}

for (int i = 0; i < nodeNum; i++){
if(data[x][i]!=0){
return i;
}
}

return -1;

}

int nextNeighbor(int x,int y){
if(x>nodeNum-1||x<0||y>nodeNum-1||y<0){
return -1;
}
for (int i = y+1; i < nodeNum; i++){
if(data[x][i]!=0){
return i;
}
}
return -1;
}

int getWeight(int x,int y){
if(x>nodeNum-1||x<0||y>nodeNum-1||y<0){
return -1;
}
return data[x][y];
}

};


void dijkstra(GraphMatrix &graph) {
const int nodeNum = 5;
const int INF = 999999;
int dist[nodeNum];
bool visited[nodeNum] = {false};

for (int i = 0; i < nodeNum; i++){
dist[i] = INF;
}
dist[0] = 0;

for (int count = 0; count < nodeNum; count++) {
int u = -1, minDist = INF;
for (int i = 0; i < nodeNum; i++) {
if (!visited[i] && dist[i] < minDist) {
minDist = dist[i];
u = i;
}
}
if (u == -1) break;

visited[u] = true;

for(int v = graph.firstNeighbor(u);v!=-1;v = graph.nextNeighbor(u, v)){
int weight = graph.getWeight(u, v);
if (!visited[v] && dist[u] != INF && dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
}
}

}

cout << "Dijkstra算法(起点0)的最短路径:" << endl;
for (int i = 0; i < nodeNum; i++) {
if (dist[i] != INF)
cout << "0->" << i << " 的最短距离: " << dist[i] << endl;
else
cout << "0->" << i << " 不可达" << endl;
}
}

void floyd(GraphMatrix &graph) {
const int nodeNum = 5;
const int INF = 999999;
int dist[nodeNum][nodeNum];

//设置一下,有边则距离设置为边的权重,反之,设置为无穷大
for (int i = 0; i < nodeNum; i++) {
for (int j = 0; j < nodeNum; j++) {
if (i == j) dist[i][j] = 0;
else {
int w = graph.getWeight(i, j);
dist[i][j] = (w != 0) ? w : INF;
}
}
}

for (int k = 0; k < nodeNum; k++){
for (int i = 0; i < nodeNum; i++){
for (int j = 0; j < nodeNum; j++){
if (dist[i][k]!=INF&&dist[k][j]!=INF&&dist[i][j]>dist[i][k]+dist[k][j]) {
dist[i][j]=dist[i][k]+dist[k][j];
}

}
}
}


cout << "\nFloyd算法的所有顶点对最短路径:" << endl;
for (int i = 0; i < nodeNum; i++) {
for (int j = 0; j < nodeNum; j++) {
if (dist[i][j] == INF) cout << "INF\t";
else cout << dist[i][j] << "\t";
}
cout << endl;
}

}

int main(){
GraphMatrix graph;
dijkstra(graph);
floyd(graph);
return 0;
}

拓扑排序

alt text

#include<iostream>
#include<queue>
#include<algorithm>
#include<vector>
using namespace std;

class GraphMatrix{
private:
int nodeNum = 5;
int data[5][5];
public:
GraphMatrix(){
for(int i=0;i<nodeNum;i++){
for(int j=0;j<nodeNum;j++){
data[i][j] = 0;
}
}
data[0][1] = 1;data[0][3] = 1;
data[1][3] = 1;data[1][2] = 1;
data[2][4] = 1;
data[3][2] = 1;data[3][4] = 1;
}

int firstNeighbor(int x){
if(x>nodeNum-1||x<0){
return -1;
}

for (int i = 0; i < nodeNum; i++){
if(data[x][i]!=0){
return i;
}
}

return -1;

}

int nextNeighbor(int x,int y){
if(x>nodeNum-1||x<0||y>nodeNum-1||y<0){
return -1;
}
for (int i = y+1; i < nodeNum; i++){
if(data[x][i]!=0){
return i;
}
}
return -1;
}

int getWeight(int x,int y){
if(x>nodeNum-1||x<0||y>nodeNum-1||y<0){
return -1;
}
return data[x][y];
}

int indegree(int x){
if(x>nodeNum-1||x<0){
return -1;
}
int result = 0;
for(int i = 0; i < nodeNum; i++) {
if(data[i][x] != 0) { // 计算入度
result++;
}
}
return result;
}

};


vector<int> topologicalSort(GraphMatrix &graph) {
const int nodeNum = 5; // 节点数固定为5
vector<int> inDegree(nodeNum);
for (int i = 0; i < nodeNum; ++i) {
inDegree[i] = graph.indegree(i);
}

queue<int> q;
for (int i = 0; i < nodeNum; ++i) {
if (inDegree[i] == 0) {
q.push(i);
}
}

vector<int> result;
while (!q.empty()){
int u = q.front();
q.pop();
result.push_back(u);
for(int v = graph.firstNeighbor(u);v!=-1;v = graph.nextNeighbor(u, v)){
if (--inDegree[v] == 0) {
q.push(v);
}
}
}

if (result.size() != nodeNum) {
return {}; // 存在环
}
return result;
}


int main() {
GraphMatrix graph;
vector<int> sorted = topologicalSort(graph);

if (sorted.empty()) {
cout << "图中存在环,无法进行拓扑排序" << endl;
} else {
cout << "拓扑排序结果:";
for (int node : sorted) {
cout << node << " ";
}
cout << endl;
}

return 0;
}

关键路径(看书吧哥,这个好复杂)

第七章 查找

基本概念

顺序查找和折半查找

顺序查找

1.一般顺序查找

很简单,直接遍历

2.有序线性表的顺序查找

也是遍历,但是如果之前的都是小于findKey,突然值大于了findKey,那么就不必再查找了,就直接停止遍历(因为是有序的)

  • 1 3 5 7 9 11 13 15 17 19 21 23 25
  • findKey = 11 遍历的时候查找到11 成功返回index
  • findKey = 12 遍历的时候查找到11之前一直小于12,但是到下一个点13就突然大于12,那么就直接返回-1(错误处理的结果)就好,查找不到

折半查找

这个就不过多介绍了

#include<iostream>
using namespace std;

int main(){

int data[10] = {2,5,7,11,13,17,19,23,29,31};
int findKey = 11;
int low=0,high=9,mid;
bool flag = false;
while (low<=high){
mid = (low + high)/2;
if(data[mid]==findKey){
cout<<"找到了! 位置为:"<<mid<<endl;
flag = true;
break;
}
if(data[mid]>findKey){
high = mid - 1;
}
if(data[mid]<findKey){
low = mid + 1;
}
}

if(!flag){
cout<<"没找到"<<endl;
}


return 0;
}

分块查找:

索引表折半,表内顺序

alt text

二叉排序树

#include<iostream>
using namespace std;

struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x):val(x),left(nullptr),right(nullptr){};
};

class BST{
private:
TreeNode* root;
//递归插入节点
TreeNode* insertNode(TreeNode* node,int val){
if(!node){
return new TreeNode(val);
}
if(val < node->val){
node->left = insertNode(node->left,val);
}else if(val > node->val){
node->right = insertNode(node->right,val);
}
//忽略重复的值
return node;
}

//辅助函数:找到子树的最小节点
TreeNode* findMin(TreeNode* node) {
while (node && node->left) node = node->left;
return node;
}


//递归删除节点
TreeNode* deleteNode(TreeNode* node,int val){
if(!node){
return nullptr;
}
if(val < node->val){
node->left = deleteNode(node->left,val);
}else if(val > node->val){
node->right = deleteNode(node->right,val);
}else{
// 情况1:无左子结点
if(!node->left){
TreeNode* right = node->right;
delete node;
return right;
}
// 情况2: 无右子节点
else if (!node->right){
TreeNode* left = node->left;
delete node;
return left;
}
// 情况3: 有两个子节点
else{
//找到右边的最小的节点
TreeNode* minRight = findMin(node->right);
//替换值并删除原始节点
node->val = minRight->val;
node->right = deleteNode(node->right,minRight->val);
}
}
return node;
}

//中序遍历
void inorderTraversal(TreeNode* node){
if (!node) return;
inorderTraversal(node->left);
cout << node->val << " ";
inorderTraversal(node->right);
}

// 递归释放节点
void destroyTree(TreeNode* node) {
if (!node) return;
destroyTree(node->left);
destroyTree(node->right);
delete node;
}

public:
BST() : root(nullptr) {}

// 插入接口
void insert(int val) {
root = insertNode(root, val);
}

// 删除接口
void remove(int val){
root = deleteNode(root,val);
}

// 查找接口
bool search(int val){
TreeNode* curr = root;
while (curr){
if(curr->val == val){
return true;
}else if (curr->val < val){
curr = curr->right;
}else{
curr = curr->left;
}
}
return false;
}

// 打印中序遍历的结果
void printInorder(){
inorderTraversal(root);
cout<<"END"<<endl;
}

// 析构函数
~BST() {
destroyTree(root);
}

};

int main(){

BST bst;

// 插入示例
bst.insert(5);
bst.insert(3);
bst.insert(7);
bst.insert(2);
bst.insert(4);
bst.insert(6);
bst.insert(8);

cout << "初始中序遍历: ";
bst.printInorder(); // 2 3 4 5 6 7 8

// 查找示例
cout << "查找 4: " << (bst.search(4) ? "存在" : "不存在") << endl; // 存在
cout << "查找 9: " << (bst.search(9) ? "存在" : "不存在") << endl; // 不存在

// 删除示例
bst.remove(3); // 删除有两个子节点的节点
cout << "删除3后的中序遍历: ";
bst.printInorder(); // 2 4 5 6 7 8

bst.remove(7); // 删除有两个子节点的节点
cout << "删除7后的中序遍历: ";
bst.printInorder(); // 2 4 5 6 8

bst.remove(2); // 删除叶子节点
cout << "删除2后的中序遍历: ";
bst.printInorder(); // 4 5 6 8
return 0;
}

平衡二叉树

#include<iostream>
#include<algorithm>
using namespace std;

struct AVLNode{
int val;
int height;
AVLNode* left;
AVLNode* right;
AVLNode(int x):val(x),height(1),left(nullptr),right(nullptr){};
};

class AVLTree{
AVLNode* root;

// 获取节点高度
int getHeight(AVLNode* node){
return node ? node->height : 0;
}

// 更新节点的高度
void updateHeight(AVLNode* node){
if(node){
node->height = 1 + max(getHeight(node->left),getHeight(node->right));
}
}

// 计算平衡因子
int getBalanceFactor(AVLNode* node) {
return getHeight(node->left) - getHeight(node->right);
}

// 右旋操作
AVLNode* rightRotate(AVLNode* y){
AVLNode* x = y->left;
AVLNode* T3 = x->right;

x->right = y;
y->left = T3;

updateHeight(y);
updateHeight(x);

return x;
}

// 左旋操作
AVLNode* leftRotate(AVLNode* x){
AVLNode* y = x->right;
AVLNode* T2 = y->left;

y->left = x;
x->right = T2;

updateHeight(x);
updateHeight(y);

return y;
}

// 插入节点
AVLNode* insertNode(AVLNode* node, int val) {
if(!node){
return new AVLNode(val);
}

if(val < node->val){
node->left = insertNode(node->left,val);
}else if(val > node->val){
node->right = insertNode(node->right,val);
}else{
return node; //不允许重复的值
}

updateHeight(node);
int balance = getBalanceFactor(node);


// 左左情况 右单旋
if (balance > 1 && val < node->left->val)
return rightRotate(node);

// 右右情况 左单旋
if (balance < -1 && val > node->right->val)
return leftRotate(node);

// 左右情况 左边先左旋 然后整体再右旋
if (balance > 1 && val > node->left->val) {
node->left = leftRotate(node->left);
return rightRotate(node);
}

return node;
}


// 删除节点(递归)
AVLNode* deleteNode(AVLNode* node, int val) {
if (!node) return nullptr;

if (val < node->val){
node->left = deleteNode(node->left, val);
}else if (val > node->val){
node->right = deleteNode(node->right, val);
}else{
//找到待删除的节点
if(!node->left||!node->right){ //子树有空的
AVLNode* temp = node->left ? node->left : node->right;
if(!temp){ // 都为空 直接删除node
temp = node;
node = nullptr;
}else{
*node = *temp; // 直接替换内容
}
delete temp;
}else{ // 子树都不为空
//和BST一样找到右边最小的节点
AVLNode* minRight = node->right;
while(minRight->left){
minRight = minRight->left;
}
node->val = minRight->val;
node->right = deleteNode(node->right,minRight->val);
}
}

if (!node) return nullptr;

// 更新高度并平衡
updateHeight(node);
int balance = getBalanceFactor(node);


// 右左情况 右边先右旋 然后整体再右旋
if (balance < -1 && val < node->right->val) {
node->right = rightRotate(node->right);
return leftRotate(node);
}

// 左左情况 右单旋
if (balance > 1 && getBalanceFactor(node->left) >= 0)
return rightRotate(node);

// 左右情况 左边先左旋 然后整体再右旋
if (balance > 1 && getBalanceFactor(node->left) < 0) {
node->left = leftRotate(node->left);
return rightRotate(node);
}

// 右右情况 左单旋
if (balance < -1 && getBalanceFactor(node->right) <= 0)
return leftRotate(node);

// 右左情况 右边先右旋 然后整体再右旋
if (balance < -1 && getBalanceFactor(node->right) > 0) {
node->right = rightRotate(node->right);
return leftRotate(node);
}

return node;
}

// 中序遍历辅助函数
void inorderTraversal(AVLNode* node) {
if (!node) return;
inorderTraversal(node->left);
cout << node->val << " ";
inorderTraversal(node->right);
}

public:
AVLTree() : root(nullptr) {}

// 插入接口
void insert(int val) {
root = insertNode(root, val);
}

// 删除接口
void remove(int val) {
root = deleteNode(root, val);
}

// 查找接口
bool search(int val) {
AVLNode* curr = root;
while (curr) {
if (curr->val == val) return true;
else if (val < curr->val) curr = curr->left;
else curr = curr->right;
}
return false;
}

// 打印中序遍历结果
void printInorder() {
inorderTraversal(root);
cout << endl;
}

};

int main(){
AVLTree avl;

// 插入测试
avl.insert(10);
avl.insert(20);
avl.insert(30);
avl.insert(40);
avl.insert(50);
avl.insert(25);

cout << "插入后的中序遍历: ";
avl.printInorder(); // 10 20 25 30 40 50

// 查找测试
cout << "查找25: " << (avl.search(25) ? "存在" : "不存在") << endl; // 存在
cout << "查找100: " << (avl.search(100) ? "存在" : "不存在") << endl; // 不存在

// 删除测试
avl.remove(30); // 删除根节点
cout << "删除30后的中序遍历: ";
avl.printInorder(); // 10 20 25 40 50

avl.remove(25); // 删除叶子节点
cout << "删除25后的中序遍历: ";
avl.printInorder(); // 10 20 40 50

return 0;
}


红黑树

#include <iostream>
using namespace std;

// 颜色枚举
enum Color { RED, BLACK };

// 红黑树节点结构
struct RBNode {
int val;
Color color;
RBNode *left, *right, *parent;
RBNode(int x) : val(x), color(RED), left(nullptr), right(nullptr), parent(nullptr) {}
};

class RedBlackTree {
private:
RBNode* root;
RBNode* nil; // 哨兵节点(表示空叶子节点)

// 左旋操作
void leftRotate(RBNode* x) {
RBNode* y = x->right;
x->right = y->left;
if (y->left != nil) {
y->left->parent = x;
}
y->parent = x->parent;
if (x->parent == nil) {
root = y;
} else if (x == x->parent->left) {
x->parent->left = y;
} else {
x->parent->right = y;
}
y->left = x;
x->parent = y;
}

// 右旋操作
void rightRotate(RBNode* x) {
RBNode* y = x->left;
x->left = y->right;
if (y->right != nil) {
y->right->parent = x;
}
y->parent = x->parent;
if (x->parent == nil) {
root = y;
} else if (x == x->parent->right) {
x->parent->right = y;
} else {
x->parent->left = y;
}
y->right = x;
x->parent = y;
}

// 插入修复(处理颜色冲突)
void insertFixup(RBNode* z) {
while (z->parent->color == RED) {
if (z->parent == z->parent->parent->left) {
RBNode* y = z->parent->parent->right; // 叔叔节点
if (y->color == RED) {
// Case 1: 叔叔是红色
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;
} else {
if (z == z->parent->right) {
// Case 2: 叔叔是黑色且z是右孩子
z = z->parent;
leftRotate(z);
}
// Case 3: 叔叔是黑色且z是左孩子
z->parent->color = BLACK;
z->parent->parent->color = RED;
rightRotate(z->parent->parent);
}
} else { // 对称操作(父节点是右孩子)
RBNode* y = z->parent->parent->left;
if (y->color == RED) {
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;
} else {
if (z == z->parent->left) {
z = z->parent;
rightRotate(z);
}
z->parent->color = BLACK;
z->parent->parent->color = RED;
leftRotate(z->parent->parent);
}
}
}
root->color = BLACK; // 根节点始终黑色
}

// 删除修复(处理颜色失衡)
void deleteFixup(RBNode* x) {
while (x != root && x->color == BLACK) {
if (x == x->parent->left) {
RBNode* w = x->parent->right; // 兄弟节点
if (w->color == RED) {
// Case 1: 兄弟是红色
w->color = BLACK;
x->parent->color = RED;
leftRotate(x->parent);
w = x->parent->right;
}
if (w->left->color == BLACK && w->right->color == BLACK) {
// Case 2: 兄弟的子节点都是黑色
w->color = RED;
x = x->parent;
} else {
if (w->right->color == BLACK) {
// Case 3: 兄弟的右孩子是黑色
w->left->color = BLACK;
w->color = RED;
rightRotate(w);
w = x->parent->right;
}
// Case 4: 兄弟的右孩子是红色
w->color = x->parent->color;
x->parent->color = BLACK;
w->right->color = BLACK;
leftRotate(x->parent);
x = root; // 终止循环
}
} else { // 对称操作(x是右孩子)
RBNode* w = x->parent->left;
if (w->color == RED) {
w->color = BLACK;
x->parent->color = RED;
rightRotate(x->parent);
w = x->parent->left;
}
if (w->right->color == BLACK && w->left->color == BLACK) {
w->color = RED;
x = x->parent;
} else {
if (w->left->color == BLACK) {
w->right->color = BLACK;
w->color = RED;
leftRotate(w);
w = x->parent->left;
}
w->color = x->parent->color;
x->parent->color = BLACK;
w->left->color = BLACK;
rightRotate(x->parent);
x = root;
}
}
}
x->color = BLACK;
}

// 查找最小节点
RBNode* minimum(RBNode* node) {
while (node->left != nil) node = node->left;
return node;
}

// 中序遍历辅助函数
void inorderTraversal(RBNode* node) {
if (node == nil) return;
inorderTraversal(node->left);
cout << node->val << (node->color == RED ? "(R) " : "(B) ");
inorderTraversal(node->right);
}

public:
RedBlackTree() {
nil = new RBNode(-1);
nil->color = BLACK;
root = nil;
}

// 插入接口
void insert(int val) {
RBNode* z = new RBNode(val);
RBNode* y = nil;
RBNode* x = root;
while (x != nil) {
y = x;
if (z->val < x->val) {
x = x->left;
} else {
x = x->right;
}
}
z->parent = y;
if (y == nil) {
root = z;
} else if (z->val < y->val) {
y->left = z;
} else {
y->right = z;
}
z->left = nil;
z->right = nil;
insertFixup(z);
}

// 删除接口
void remove(int val) {
RBNode* z = root;
while (z != nil) {
if (z->val == val) break;
else if (val < z->val) z = z->left;
else z = z->right;
}
if (z == nil) return; // 未找到

RBNode* y = z;
Color yOriginalColor = y->color;
RBNode* x;

if (z->left == nil) {
x = z->right;
transplant(z, z->right);
} else if (z->right == nil) {
x = z->left;
transplant(z, z->left);
} else {
y = minimum(z->right);
yOriginalColor = y->color;
x = y->right;
if (y->parent == z) {
x->parent = y;
} else {
transplant(y, y->right);
y->right = z->right;
y->right->parent = y;
}
transplant(z, y);
y->left = z->left;
y->left->parent = y;
y->color = z->color;
}
delete z;
if (yOriginalColor == BLACK) {
deleteFixup(x);
}
}

// 替换节点辅助函数
void transplant(RBNode* u, RBNode* v) {
if (u->parent == nil) {
root = v;
} else if (u == u->parent->left) {
u->parent->left = v;
} else {
u->parent->right = v;
}
v->parent = u->parent;
}

// 查找接口
bool search(int val) {
RBNode* curr = root;
while (curr != nil) {
if (curr->val == val) return true;
else if (val < curr->val) curr = curr->left;
else curr = curr->right;
}
return false;
}

// 打印中序遍历结果
void printInorder() {
inorderTraversal(root);
cout << endl;
}
};

int main() {
RedBlackTree rbt;

// 插入测试
rbt.insert(10);
rbt.insert(20);
rbt.insert(30);
rbt.insert(40);
rbt.insert(50);
rbt.insert(25);

cout << "插入后的中序遍历: ";
rbt.printInorder(); // 输出示例:10(B) 20(R) 25(R) 30(B) 40(R) 50(R)

// 查找测试
cout << "查找25: " << (rbt.search(25) ? "存在" : "不存在") << endl; // 存在
cout << "查找100: " << (rbt.search(100) ? "存在" : "不存在") << endl; // 不存在

// 删除测试
rbt.remove(30); // 删除根节点
cout << "删除30后的中序遍历: ";
rbt.printInorder(); // 示例输出:10(B) 20(R) 25(B) 40(R) 50(R)

rbt.remove(25); // 删除叶子节点
cout << "删除25后的中序遍历: ";
rbt.printInorder(); // 示例输出:10(B) 20(B) 40(R) 50(R)

return 0;
}

hush表

#include<iostream>
#include<string>
#include<vector>
using namespace std;

const int TABLE_SIZE = 10; // 固定大小的哈希表

struct HashNode{
string key;
int value;
HashNode* next;
HashNode(const string& k,int v):key(k),value(v),next(nullptr){}
};

class HashTable{
private:
vector<HashNode*> buckets; // 桶数组

// 哈希函数(简单示例)
int hashFunction(const string& key){
int hash = 0;
for(char c:key){
hash += c;
}
return hash % TABLE_SIZE;
}
public:
HashTable() : buckets(TABLE_SIZE, nullptr) {}
~HashTable(){
// 析构时释放所有节点
for(int i = 0; i < TABLE_SIZE; ++i){
HashNode* entry = buckets[i];
while(entry){
HashNode* prev = entry;
entry = entry->next;
delete entry;
}
}
}

// 插入键值对
void insert(const string& key, int value) {
int bucketIndex = hashFunction(key);
HashNode* entry = buckets[bucketIndex];
while (entry){
if (entry->key == key) {
entry->value = value; // 更新现有值
return;
}
entry = entry->next;
}
// 创建新节点并插入链表头部
HashNode* newNode = new HashNode(key, value);
newNode->next = buckets[bucketIndex]; //头插法
buckets[bucketIndex] = newNode;
}

// 获取值
int get(const string& key) {
int bucketIndex = hashFunction(key);
HashNode* entry = buckets[bucketIndex];
while(entry){
if(entry->key == key){
return entry->value;
}
entry = entry->next;
}
return -1;// 未找到返回-1
}

// 删除键
void remove(const string& key){
int bucketIndex = hashFunction(key);
HashNode* entry = buckets[bucketIndex];
HashNode* prev = nullptr;
while(entry){
if(entry->key == key){
if(prev){
prev->next = entry->next;
}else{
buckets[bucketIndex] = entry->next;
}
delete entry;
}
prev = entry;
entry = entry->next;
}
}
};

int main() {
HashTable myHashTable;

myHashTable.insert("Alice", 30);
myHashTable.insert("Bob", 25);
myHashTable.insert("Charlie", 28);

cout << "Alice's age: " << myHashTable.get("Alice") << endl; // 30
cout << "Unknown key: " << myHashTable.get("Dave") << endl; // -1

myHashTable.remove("Bob");
cout << "After removal - Bob: " << myHashTable.get("Bob") << endl; // -1

return 0;
}

第八章 查找

8.2 插入排序

直接插入排序

#include<iostream>
using namespace std;

int main(){

int data_1[11] = {36,27,20,60,55,7,70,36,44,67,16};

for(int i=1;i<11;i++){
for(int j=i;j>0;j--){
if(data_1[j]<data_1[j-1]){
int temp = data_1[j-1];
data_1[j-1] = data_1[j];
data_1[j] = temp;
}
}
}

for(int i=0;i<11;i++){
cout<<data_1[i]<<" ";
}
cout<<endl;


int data_2[11] = {36,27,20,60,55,7,70,36,44,67,16};
for(int i=1;i<11;i++){
int key = data_2[i];
int j = i;
while(j > 0&& key > data_2[j-1]){
data_2[j] = data_2[j-1];
j--;
}

data_2[j] = key;
}

for(int i=0;i<11;i++){
cout<<data_2[i]<<" ";
}
cout<<endl;


return 0;
}

折半插入排序

#include<iostream>
using namespace std;

int main(){

int data[11] = {36,27,20,60,55,7,70,36,44,67,16};

for(int i=1;i<11;i++){
int key = data[i];
int low = 0,high = i-1;
//通过折半查找找到插入位置
while(low <= high){
int mid = (low + high)/2;
if(key > data[mid]){
high = mid - 1;
}else{
low = mid + 1;
}
}

for(int j=i;j>high+1;j--){
data[j] = data[j-1];
}
data[high + 1] = key;
}


for(int i=0;i<11;i++){
cout<<data[i]<<" ";
}
cout<<endl;


return 0;
}

希儿排序

#include<iostream>
using namespace std;

int main(){
int n = 11;
int data[n] = {36,27,20,60,55,7,70,36,44,67,16};
// 初始间隔为n/2,每次减半直到1
for (int gap = n/2; gap > 0; gap /= 2) {
//每一组内部还是使用插入排序(这里直接使用的是直接插入排序,折半的话要处理下标,很麻烦)
for(int i = gap;i<n;i++){
int tmp = data[i];
int j = i;
while(j>0&&j>=gap&&data[j-gap]>tmp){
data[j] = data[j-gap];
j = j - gap;
}
data[j] = tmp;
}
}


for(int i=0;i<11;i++){
cout<<data[i]<<" ";
}
cout<<endl;
}

8.3 交换排序

冒泡排序

#include<iostream>
using namespace std;

int main(){
int n = 11;
int data[n] = {36,27,20,60,55,7,70,36,44,67,16};

for(int i=0;i<n-1;i++){//确定第i个位置的数据
bool flag = false; //标志是否发生交换,如果没有发生交换就代表着已经有序了
for(int j=n-1;j>i;j--){ //从最后面,一直往前面冒泡
if(data[j]<data[j-1]){
int temp = data[j-1];
data[j-1] = data[j];
data[j] = temp;
flag = true;
}
}

if(!flag){
break;
}
}

for(int i=0;i<11;i++){
cout<<data[i]<<" ";
}
cout<<endl;

return 0;
}

快速排序

#include<iostream>
using namespace std;

int partition(int data[],int low,int high){
int pivot = data[low];
while(low<high){
//注意这里一定要先操作high 因为 low位置是空的插槽
while(low<high&&data[high]>=pivot){
high--;
}
data[low] = data[high]; //high位置空出来了插槽
while(low<high&&data[low]<=pivot){
low++;
}
data[high] = data[low]; //low位置空出来了插槽
}
//此时low=high了,就是要分割的位置
data[low] = pivot;
return low;
}

void quickSort(int data[],int low,int high){
if(low<high){
int pivotpos = partition(data,low,high);
quickSort(data,low,pivotpos-1);
quickSort(data,pivotpos+1,high);
}
}

int main(){
int n = 11;
int data[n] = {36,27,20,60,55,7,70,36,44,67,16};
quickSort(data,0,n-1);

for(int i=0;i<n;i++){
cout<<data[i]<<" ";
}
cout<<endl;

return 0;
}

8.4 选择排序

简单选择排序

#include<iostream>
using namespace std;

int main(){
int n = 11;
int data[n] = {36,27,20,60,55,7,70,36,44,67,16};

for(int i=0;i<n-1;i++){
int minIndex = i;
for(int j=i+1;j<n;j++){
if(data[minIndex]>data[j]){
minIndex = j;
}
}
if(minIndex!=i){
int temp = data[i];
data[i] = data[minIndex];
data[minIndex] = temp;
}
}

for(int i=0;i<n;i++){
cout<<data[i]<<" ";
}
cout<<endl;


return 0;
}

快排

#include<iostream>
using namespace std;

int partition(int data[],int low,int high){
int pivot = data[low];
while(low<high){
//注意这里一定要先操作high 因为 low位置是空的插槽
while(low<high&&data[high]>=pivot){
high--;
}
data[low] = data[high]; //high位置空出来了插槽
while(low<high&&data[low]<=pivot){
low++;
}
data[high] = data[low]; //low位置空出来了插槽
}
//此时low=high了,就是要分割的位置
data[low] = pivot;
return low;
}

void quickSort(int data[],int low,int high){
if(low<high){
int pivotpos = partition(data,low,high);
quickSort(data,low,pivotpos-1);
quickSort(data,pivotpos+1,high);
}
}

int main(){
int n = 11;
int data[n] = {36,27,20,60,55,7,70,36,44,67,16};
quickSort(data,0,n-1);

for(int i=0;i<n;i++){
cout<<data[i]<<" ";
}
cout<<endl;

return 0;
}

8.5 归并与基数

归并排序

#include<iostream>
using namespace std;

void merge(int data[],int left,int right,int mid){
int n1 = mid - left + 1; //将中间元素归于左边了
int n2 = right - mid;

int leftArr[n1] = {0};
int rightArr[n2] = {0};

for(int i=0;i<n1;i++){
leftArr[i] = data[i+left];
}

for(int j=0;j<n2;j++){
rightArr[j] = data[j+mid+1];
}

//归并
int i = 0,j=0,k=left;
while(i<n1&&j<n2){
if(leftArr[i]<=rightArr[j]){
data[k] = leftArr[i];
i++;
}else{
data[k] = rightArr[j];
j++;
}
k++;
}
//把较长的那个未检测完的那个复制一下
while(i<n1){
data[k++] = leftArr[i++];
}
while(j<n2){
data[k++] = rightArr[j++];
}

}

void mergeSortRecursive(int data[],int left,int right){
if(left<right){
int mid = (left + right) / 2;
mergeSortRecursive(data,left,mid);
mergeSortRecursive(data,mid+1,right);
merge(data,left,right,mid);
}
}

// 非递归实现 //自下而上
void mergeSortIterative(int data[], int n) {
for (int size = 1; size < n; size *= 2) {
//注意这里为啥要left+ 2 * size,因为一次要合并俩数组,左边一个size,右边一个size
for(int left=0;left<n;left+=2*size){
int mid = min(left+size-1,n-1);
int right = min(left+2*size-1,n-1);
if(mid<right){
merge(data,left,right,mid);
}
}
}
}

int main(){
int n = 11;
int data_1[n] = {36,27,20,60,55,7,70,36,44,67,16};

mergeSortRecursive(data_1, 0, n - 1);
cout << "递归结果: ";
for(int i=0;i<n;i++){
cout<<data_1[i]<<" ";
}
cout<<endl;

int data_2[n] = {36,27,20,60,55,7,70,36,44,67,16};
mergeSortIterative(data_2, n);
cout << "递归结果: ";
for(int i=0;i<n;i++){
cout<<data_2[i]<<" ";
}
cout<<endl;

return 0;
}