算法基础
- 递归算法的空间复杂度 = 每次递归的空间复杂度 * 递归深度
- c/c++的内存管理
- 固定部分:
- 代码区:存放二进制代码
- 数据区:全局变量,静态变量和常量等等
- 可以变部分
- 栈区:运行方法的形参,局部变量,返回值,以及递归栈所需的空间.系统自动分配和回收
- 堆区:动态开辟的空间,存放new出来的对象在堆区中的真实数据.需要手动回收
- 固定部分:
- 内存对齐:
- 平台原因:为了同一个程序可以在多平台运行,需要内存对齐
- 硬件原因:经过内存对齐后,CPU访问内存的速度大大提升
- 内存对齐:一次寻址即可. 非内存对齐:两次寻址+一次合并操作
- 数组是存放在连续内存空间上的相同类型数据的集合,删除/添加元素要移动其他元素
- 有序不唯一元素是二分法的前提条件
- 算法特征:
- 明确性:算法应该清晰明确.每个步骤及其输入/输出明确并只能产生一种含义
- 输入:应该有0个或以上定义明确的输入
- 输出:应该有1个或以上明确定义和匹配所需的输出
- 有限性:必须在有限步骤后终止
- 可行性:在可用资源下是可行的
- 独立性:有分步知道,独立于任何编程代码
- 贪婪算法:
- 贪心算法试图找到局部最优解,这可能最终导致全局优化解。然而,一般贪婪算法不提供全局优化的解决方案。
- 实例:
- 动态规划之TSP(Travel Salesman Problem)算法:https://blog.csdn.net/diaosi1446427351/article/details/80384279
- 最小生成树算法(普里姆算法 Prim`s algorithm) https://blog.csdn.net/feliciafay/article/details/19150519
- Kruskal’s Minimum Spanning Tree Algorithm 最小生成树 https://www.cnblogs.com/mqxnongmin/p/10542681.html
- 最短路径算法之Dijkstra's Minimal Spanning Tree Algorithm https://blog.csdn.net/qq_42370259/article/details/81981857
- Graph - Map Coloring https://www.includehelp.com/algorithms/graph-coloring-problem-solution-using-backtracking-algorithm.aspx
- Graph - Vertex Cover 无向图计算——最小顶点覆盖 https://blog.csdn.net/qq_39825375/article/details/103132112
- 背包问题(Knapsack Problem)https://blog.csdn.net/weixin_40756041/article/details/88053823
- Job Scheduling Problem作业车间调度问题 https://blog.csdn.net/Jayphone17/article/details/103434555
- 分治算法:
- 1.划分;2.解决;3.合并
- 实例:
- Merge sort(归并排序) https://blog.csdn.net/a130737/article/details/38228369
- Quick Sort(快速排序) https://www.cnblogs.com/lingnan/p/4900297.html
- Binary Search(二分法查找) https://www.cnblogs.com/wkfvawl/p/9475939.html
- Strassen's Matrix Multiplication(矩阵相乘算法)
- Closest pair (points) 最近点对问题 https://blog.csdn.net/u013522065/article/details/44810915
- 动态规划:
- 类似于分治法,将问题分解为更小的可能的子问题。这些较小子问题的结果会被记住并用于相似或重叠的子问题
- 实例:
- Fibonacci number series(斐波那契数列) https://www.cnblogs.com/garrettlu/p/10064932.html
- 背包问题(Knapsack Problem)
- Tower of Hanoi(汉诺塔问题) https://blog.csdn.net/qq_39980334/article/details/104276562
- All pair shortest path by Floyd-Warshall http://alrightchiu.github.io/SecondRound/all-pairs-shortest-pathfloyd-warshall-algorithm.html
- 最短路径(Shortest Path)- Dijkstra(迪杰斯特拉算法) https://blog.csdn.net/songzhuo1991/article/details/103072113
- Project scheduling(进程调度算法) https://blog.csdn.net/qq_45913057/article/details/109690121
- 派生数据类型:List, Array, Stack, Queue
- 基本操作:Traversing(遍历),Searching,Insertion, Deletion, Sorting, Merging
- 数组:
- 链表:链表是一系列数据结构,它们通过链接连接在一起。
- 单向链表:
- 链接:链表的每个链接都可以存储称为元素的数据。
- 链表的每个链接都包含一个链接到名为 Next 的下一个链接。
- 链表包含到第一个名为 First 的链接的连接链接。
- 双向链表(Doubly Linked List):
- 比单向链表多一个Prev
- 操作:插入开始,删除开始,插入最后,删除最后,后插入,根据key删除,正向显示,向后显示
- 循环链表:最后一个元素指向第一个元素
- 操作:插入,删除,显示
- 单向链表:
- 堆栈:堆栈是一种抽象数据类型 (ADT),通常用于大多数编程语言。
- 操作:push,pop, peek, isFull, isEmpty
- 队列:Queue 是一种抽象的数据结构,FIFO
- 操作:enqueue, dequeue,peek,isfull, isempty
- 查找:
- 线性搜索是一种非常简单的搜索算法
- 二分查找(Binary search):必须对目标数组进行排序
- 步骤:
- 1.找出中位数 mid
- 2.目标target 与mid比较
- 3.对比成功或者查无此目标
- 步骤:
- 插值搜索(Interpolation search):此搜索算法适用于所需值的探测位置
- mid = Lo + ((Hi - Lo) / (A[Hi] - A[Lo])) * (T - A[Lo])
- 步骤:步骤跟二分查找一样
- 哈希表是一种以关联方式存储数据的数据结构,在哈希表中,数据以数组格式存储,其中每个数据值都有自己唯一的索引值
- 插入和搜索操作都非常快的数据结构
- 散列是一种将一系列键值转换为数组索引范围的技术 hash:12 % 20 = 12
- 通过查看下一个单元格来搜索数组中的下一个空位置,直到找到一个空单元格。这种技术称为线性探测。
- 排序算法:
- 冒泡排序(Bubble sort) Ο(n^2)
- 插入排序:(Insertion sort) Ο(n^2)
- 1.每两个比较一次
- 2.每比较完一次都有对所有已比较过的都比较一次
- 选择排序:将列表分为两部分,左端的已排序部分和右端的未排序部分 Ο(n^2)
- 1.选择最小的放在最左边
- 归并排序:一种基于分治技术的排序技术 Ο(n log n)
- 1.分成单个元素
- 2.对比合并
- 希尔排序(Shell sort): 最好情况 O(n)
- 如果较小的值在最右侧并且必须移到最左侧,则该算法避免了在插入排序的情况下的大移位
- 1.h = h * 3 + 1
- 2.将列表划分为等间隔 h 的较小子列表
- 3.使用插入排序对这些子列表进行排序
- 快速排序是一种高效的排序算法,它基于将数据数组划分为更小的数组
- 步骤1:
- 1.选择最高的索引值pv
- 2.取最左lo和最右hi不包括pv
- 3.lo小于pv,右移; 右值大于pv,左移
- 4.如果第三步都为FALSE,交换lo和hi
- 5.如果lo>=hi,则交换lo和pv;重复
- 步骤2:(递归)
- 1.最右边的索引值pv
- 2.以pv分组
- 3.递归快速排序左分区
- 4.递归快速排序右分区
- 步骤1:
- 图:是一组对象的图形表示,其中一些对象对通过链接连接(顶点(Vertex),边(Edge),相邻(Adjacency),路径(path))
- 深度优先搜索(Depth First Search (DFS)):以深度运动方式遍历图,并使用堆栈来记住在任何迭代中出现死胡同时获取下一个顶点以开始搜索。
- 1.初始化栈
- 2.第一个顶点压入栈,第二个..回到第一个顶点停止
- 3.出栈并检查是否有未访问节点,有的话执行步骤2
- 重复123,直到全部出栈
- 广度优先搜索 (BFS) 算法以广度运动遍历图,并使用队列记住在任何迭代中出现死胡同时获取下一个顶点以开始搜索。
- 1.初始化队列
- 2.标记起始点S,S相邻未访问的点添加到队列(FIFO)
- 3.第一个入队的点,移出并检查未访问点,将其入队,重复执行步骤2
- 4.重复123,直到全部出队
- 深度优先搜索(Depth First Search (DFS)):以深度运动方式遍历图,并使用堆栈来记住在任何迭代中出现死胡同时获取下一个顶点以开始搜索。
- 树:(Path,Root,Parent,Child,Leaf,Subtree,Visiting,Traversing,Levels,Keys)
- 遍历树:
- 有序遍历(In-order Traversal)
- 递归左边子树(左下->中上->右下)
- 根节点
- 递归右边子树(左下->中上->右下)
- 前序遍历(Pre-order Traversal)
- 根节点
- 递归左边子树(中上->左下->右下)
- 递归右边子树(中上->左下->右下)
- 后序遍历(Post-order Traversal)
- 递归左边子树(左下->右下->中上)
- 递归右边边子树(左下->右下->中上)
- 根节点
- 有序遍历(In-order Traversal)
- 二叉搜索树(BST):
- 特性:
- 左子树的键值小于其父(根)节点的键值。
- 右子树的键值大于或等于其父(根)节点的键值。
- 操作:
- 遍历树
- 查找
- 插入
- 特性:
- [平衡]AVL二叉树(AVL Trees):BalanceFactor = height(left-sutree) − height(right-sutree)
- 左旋转:节点插入到右子树,执行左旋转
- 右下B与中上A旋转调整为左下A和B,左下B与左下C旋转调整为中上B和右下C
- 右旋转:节点插入到左子树,执行右旋转
- 左下B与中上C旋转调整为右下B和C,右下B与右下C旋转调整为中上B和左下C
- 左旋转:节点插入到右子树,执行左旋转
- 生成树是图 G 的一个子集,它的所有顶点都被尽可能少的边数覆盖。因此,生成树没有环,也不能断开。
- 一个无向图最多有n^(n-2)个生成树
- 从生成树中删除一条边将使图断开连接,即生成树是最小连接的。
- 向生成树添加一条边将创建一个回路或环路,即生成树最大程度地是非循环的。
- 应用:
- 民用网络规划
- 计算机网络路由协议
- 聚类分析
- 堆是平衡二叉树数据结构的一种特殊情况,其中根节点键与其子节点进行比较并相应地排列
- Min-Heap - 根节点的值小于或等于其任一子节点。
- Max-Heap - 根节点的值大于或等于其任一子节点。
- 最大堆构建算法:
- 1.在堆的末尾创建一个新节点。
- 2.为节点分配新值。
- 3.将此子节点的值与其父节点的值进行比较。
- 4.如果父值小于子值,则交换它们。
- 5.重复第 3 步和第 4 步,直到 Heap 属性成立。
- 最小生成树算法:
- Prim 寻找最小成本生成树的算法(如 Kruskal 算法)使用贪婪方法。 Prim 算法与最短路径优先算法有相似之处。
- Kruskal 寻找最小成本生成树的算法使用贪婪方法。该算法将图视为森林,并将其拥有的每个节点视为一棵树。一棵树仅连接到另一个树,并且仅当它在所有可用选项中成本最低且不违反 MST 属性。
- 遍历树:
数据结构与操作
链表
- 单向链表
- 结构:
struct node{ int data; int key; struct node *next; }
- 操作:
- while(ptr!=NULL){ptr = ptr->next}
- insertFirst(){*link = (struct node*) malloc(sizeof(struct node));head = link;}
- deleFirst(){head=head->next}
- isEmpty(){return head==NULL}
- length(){while(!current){length++;current=current->next}}
- find(){while(current->key != key){return current}}
- deleteByKey(){previous->next=current->next}
- sort(){}
- reverse(){}
算法步骤
- 二分法步骤
- 初始化 left=0,right=size-1,middle=left+(right-left)/2
- 不断缩小left到right的范围,找到target
- 双指针1(移除元素):
- 初始化slowIndex=0,fastIndex=0
- 如果[fastIndex]!=target,[slowIndex++]=[fastIndex]
- 双指针2(有序数组的平方):
- 初始化lowestIndex=0,highestIndex=size-1
- 滑动窗口(长度最小数组之和>=target)
- 初始化slowIndex=0,fastIndex=0
- 如果slowIndex到fastIndex之和大于等于target,slowIndex前移
- 链表
- 删除D节点:C->next = D->next;free(D)
- 添加F节点:C->next=F;F->next=D;
- 删除链表元素
- 删除头结点
- 新建tmp=head;
- head = head->next;(向前移一位)
- delete tmp;
- 删除非头部节点
- 新建cur=head;
- 新建tmp = cur->next;(删除非头部+1.得出)
- cur->next=cur->next->next;
- delete tmp;
- 删除头结点
- 翻转链表
- 定义一个cur=head,pre=null
- tmp=cur->next;cur->next = pre;
- pre=cur; cur = tmp;
- 链表相交
- 两链表的end()对齐
- 找出curA==curB
- 环形链表
- 哈希表(Hash table):一般用来快速判断一个元素是否出现在集合里
- 栈和队列
- 二叉树(binary tree)
- 满二叉树:深度为k,有2^k-1个节点的二叉树
- 完全二叉树:允许右叶子缺失的满二叉树
- 二叉树遍历:
- 中序遍历:cur=root;cur.left==NULL ? stack.push(cur):cur=cur.left;
- 前序遍历:stack.push(root);cur=stack.pop();stack.push(cur.left);stack.push(cur.right)
- 后序遍历:用两个栈
- 层序遍历(图论中的广度优先遍历):
- que.push(root);node = que.front();que.pop();que.push(node.left);que.push(node.right);
- 回溯算法
- 理论基础
- 组合,分割,子集,排列,棋盘问题,其他.
- 回溯算法是一种搜索方式.有递归就有回溯
- 组合
- 理论基础