基础算法
本专栏是关于基础算法和数据结构的,如数组、链表、栈、队列、树等。还有一些经典算法题目。适合算法初学者,欢迎订阅。
云镛
终身学习者
展开
-
用递归实现冒泡排序
用递归实现冒泡排序本文内容如标题所示,用递归实现冒泡排序,相信你对冒泡排序不陌生,也实现过,不知道你用递归实现过没?下面就看下其代码://用递归实现输出数组void output(int arr[], int n){ static int i = 1; if(i == n){ printf("%d\n",arr[n - 1]); //此处的i = 1不能省略,否则排序后第二次输出时,就会输出不全,自己可以试试. i = 1;原创 2020-11-19 10:24:25 · 7497 阅读 · 0 评论 -
快速排序及其优化和快速选择算法
快速排序及其优化和快速选择算法本文主要内容是快速排序的代码编写及其优化,还有快速选择算法(在无序的数中寻找第k大或小的元素)上代码:#include <iostream>#include <cstdio>#include <cstdlib>#include <queue>#include <stack>#include <algorithm>#include <string>#include &l原创 2020-11-16 15:54:35 · 299 阅读 · 0 评论 -
SBTree树实现
SBTree树实现SBTree与AVL树具有相同的概念,也具有相同的操作,类似自平衡,旋转操作和旋转触发。SBTree严格遵守下列公式,如有违反,则需要通过相应的转置操作来达到平衡.SIZE[right[t]]>=max(SIZE[left[left[t]]],SIZE[right[left[t]]]);SIZE[left[t]]>=max(SIZE[left[right[t]]],SIZE[right[right[t]]]);其中SIZE[t]表示t结点所在的子树个数,...原创 2020-11-12 10:31:05 · 397 阅读 · 0 评论 -
寻找二叉搜索树中给定节点的下一个节点
寻找二叉搜索树中给定节点的下一个节点题目:给定一个二叉搜索树,节点带有指向父节点的指针域,以中序遍历的方式排序该树,求给定节点的下一个节点。参考树:bool isLeftNode(TreeNode *node,TreeNode *parent){ return parent->left == node;}TreeNode *leftMostNode(TreeNode *node){ if(node == NULL) return NULL;原创 2020-11-04 21:49:32 · 427 阅读 · 0 评论 -
Path Sum
Path Sum题目:Get all the paths(always starts from the root) in a binary tree,whose sum would be equal to given value.void pathSumHelper(vector<int> &path,vector<vector<int>> &result,TreeNode *root,int sum){ if(root == NULL)原创 2020-10-30 16:16:23 · 136 阅读 · 0 评论 -
判断Tree2是否是Tree1的子树
判断Tree2是否是Tree1的子树题目:Tree1 and Tree2 are both binary trees nodes having value, determine if Tree2 is a subtree of tree1.bool matchTree(TreeNode *roo1,TreeNode *root2){ if(root1 == NULL && root2 == NULL) return true; if(root1 ==原创 2020-10-30 15:23:51 · 136 阅读 · 0 评论 -
如何判断一个树是否是二叉搜索树
如何判断一个树是否是二叉搜索树一个小技巧:可以同时传入最小/最大值,并且将初始值设为INT_MIN,INT_MAX,这样,其左子树所有节点的值必须在INT_MIN及根节点的值之间,其右子树所有节点的值必须在根节点的值以及INT_MAX之间。bool helper(TreeNode *root,int min,int max){ if(root == NULL) return true; if((root->val < max || (root->val ==原创 2020-10-30 15:00:28 · 625 阅读 · 0 评论 -
二叉树:判断一个树是否是平衡二叉树
判断一个树是否是平衡二叉树题目:给定一个二叉树,判断其是否是平衡二叉树。方法一:bool isBalancedTree(TreeNode* root){ if(root == NULL) return true; int Lh = getHeight(root->left); int Rh = getHeight(root->right); int h = Lh - Rh; if(h < -1 || h > 1)原创 2020-10-15 17:51:19 · 706 阅读 · 0 评论 -
栈和队列:用栈求解表达式的值
用栈求解给定表达式的值题目:求解给定表达式的值#include <iostream>#include <string>#include <stack>#include <stdlib.h>using namespace std;//栈外优先级 int icp(char c){ switch(c) { case'+':return 2; case'-':return 2; case'*':return原创 2020-09-18 15:25:32 · 337 阅读 · 0 评论 -
栈和队列:层序遍历二叉树
层序遍历二叉树题目:层序遍历二叉树。思路:用队列保存当前节点的非空子节点。void levelTravel(TreeNode *root){ if(root == NULL) { return; } queue<TreeNode*> q; q.push(root); while(!q.empty()) { root = q.front(); visit(root);原创 2020-09-17 10:45:44 · 267 阅读 · 0 评论 -
栈和队列:用栈实现二叉树的后序遍历
用栈实现二叉树的后序遍历题目:用栈实现二叉树的后序遍历//用两个栈,一个是辅助栈,一个用来存储结果void postOrder(TreeNode *root){ stack<TreeNode*> helper; stack<TreeNode*> output; if(root == NULL) return; while(root || !helper.empty()) { if(root)原创 2020-09-16 11:18:37 · 665 阅读 · 0 评论 -
栈和队列:二叉树的前序遍历
二叉树的前序遍历题目:用两种方式实现二叉树的前序遍历方法1:递归void preOrder(TreeNode *root){ if(root == NULL) return; visit(root); preOrder(root->left); preOrder(root->right);}方法2:用栈实现前序遍历vector<int> preOrder(TreeNode *root){ vecto原创 2020-09-14 16:21:29 · 757 阅读 · 0 评论 -
栈和队列:计算表达式
计算表达式题目:计算(4+5)*(7-2)思路:本文只介绍简单处理后的表达式,来简单的阐明栈在计算表达式中的应用,例如上面的表达式,要用栈的话,需要对表达式做转换,转成4 5 + 7 2 - *,然后利用栈做计算。int express(string input){ stack<int> st; int op1,op2; int i=0; while(i < input.size()) { if(input[i] =原创 2020-09-12 18:49:17 · 441 阅读 · 0 评论 -
栈和队列:中序遍历一个二叉树
中序遍历一个二叉树题目:两种方式遍历给定二叉树方法一:递归本方法代码很简单,可见算法的精髓就在于很巧妙的解决问题,并不需要很复杂的代码。void InOrder(TreeNode *root){ if(root == NULL) return; InOrder(root->left); visit(root); InOrder(root->right);}方法二:用stack临时存储前言:用stack解决Top-Dow原创 2020-09-11 17:40:34 · 271 阅读 · 0 评论 -
栈和队列:Validate Parenthesis
Validate Parenthesis前言:有一类问题有这样的特性:当前节点的解决依赖后驱节点。对于某个当前节点,如果不能获知后驱节点,就无法得到有意义的解。这类问题可以通过stack(或等同于stack的若干个临时变量)解决:先将当前节点入栈,然后看其后继节点的值,直到其依赖的所有节点都完备时,再从栈中弹出该节点求解。某些时候,甚至需要反复这个过程:将当前节点的计算结果再次入栈,直到其依赖的后继节点完备。看一个例子:Validate ParenthesisGiven原创 2020-09-10 22:31:57 · 90 阅读 · 0 评论 -
栈和队列:排序一个栈内的元素
排序一个栈内的元素题目:用一个额外的栈以升序的方式排序栈的元素,升序在这里指出栈的元素以升序排列。//整个思想类似于插入排序stack<int> sort(stack<int> &input){ stack<int> output; //非空 while(!input.empty()) { int val = input.top(); input.pop(); //不为空原创 2020-09-09 22:19:09 · 249 阅读 · 0 评论 -
栈和队列:用队列实现一个栈
用队列实现一个栈题目:用两个队列实现一个栈.思路:保持一个队列为空,一个队列有数据,插入数据时向有数的队列插,弹出数据时,将有数队列的最后一个留下,其余的挪动到空队列。class MyStack{ private: queue<int> q1; queue<int> q2; public: void push(int val); int pop();};//向不为空的队列插原创 2020-09-08 11:54:01 · 137 阅读 · 0 评论 -
栈和队列:用栈实现一个队列
用栈实现一个队列题目:用栈实现一个队列class QueueWithStack{ private: stack<int> input; stack<int> output; public: int pop(); void push(int);};void QueueWithStack::push(int value){ input.push(value);}int Queu原创 2020-09-07 21:12:42 · 148 阅读 · 0 评论 -
栈和队列:实现一个栈,O(1)时间复杂度获取栈内最大元素
实现一个栈,获取栈内最大元素题目:实现一个栈,使栈的Push、Pop Top和Max的时间复杂度为O(1),Max()将返回栈内最大的元素。思路:用两个栈,第一个是常规栈,第二个只存储到来的元素中比栈顶大的元素。复杂度分析:时间复杂度符合题目要求O(1)。空间复杂度最坏情况附加的stack中需要储存每个元素,故额外使用O(n)空间。class stackWithMax{ private: stack<int> valueStack;原创 2020-09-06 19:50:31 · 1389 阅读 · 0 评论 -
十大简单排序:基数排序
基数排序思想:先找出最大数,确定数据的位数,然后依次按个位数计数排序、十位数计数排序、百位数计数排序....直到按所有的位数排序。最后的结果就是排序好的数列。看一个示例:排序 123,211,321, 145按个位数排序:211,321,123,145按十位数排序:211,321,123,145按百位数排序:123,145,211,321上述示例是3位数的排序,我们看下比较通用的算法。void sort(int arr[],int len){ int max = ar原创 2020-09-05 10:43:43 · 236 阅读 · 0 评论 -
十大简单排序:桶排序
桶排序本次介绍的这个桶排序,用的比较少,下面分析其思想。思想:遍历数组,找到数组中的最大值和最小值,计算差值,然后选择桶的个数,有了差值和桶个数,进而可以确定每个桶的取值范围,之后再次遍历数组,将元素放入桶中,对每个桶内的元素排序,最后依次输出桶内值,如果桶用数组表示,那么数组的大小必须是原数组的大小,因为最坏情况下所有数在一个桶,这样的话,空间复杂度就高了,如果桶用链表的话,可以省空间,但是对桶内数据进行排序的时候,时间复杂度是O(N^2)。时间上又浪费,综上所述,省时间费空间,省空间费时间.所以原创 2020-09-04 18:18:26 · 296 阅读 · 0 评论 -
十大简单排序:计数排序
计数排序思想:用一个例子来说明计数排序的思想,对N个50-59之间的整数进行排序,N可能很大。虽然数据量很大,但是取值范围却很小,我们可以利用一个数组a[10],a[0].....a[9]对应50....59,也就是说,数据减去50作为数组的下标,例如56,那么对应的下标就是56-50=6,那么a[6]执行一次加1操作,代表出现了56这个数,对数组中的所有元素都是一个道理,最终a[0]的数值就是原数组中50的个数,同理a[1]...a[9]。最后遍历a[10]数组,输出相应的值即可。代码如下。in原创 2020-09-03 14:36:15 · 216 阅读 · 0 评论 -
十大简单排序:归并排序
归并排序思想:先把数组中的元素拆分成两部分,使这两部分都有序,然后再合并这两部分。其中拆分成的两部分,又可以继续拆分成两部分,如此递归下去,直到不能拆分。void merge(int arr[],int leftptr,int rightptr,int rightBound){ int mid = rightptr - 1; int i = leftptr; int j = rightptr; int k = 0; int *newArr = new in原创 2020-09-02 16:20:43 · 102 阅读 · 0 评论 -
十大简单排序:快速排序
快速排序(分治思想)思想:选定一个元素做中轴,一般是数组最后一个元素,从两头遍历数组,左边比中轴大的元素和右边小于等于中轴的元素对换,当左游标大于等于右游标的时候,左游标处的值一定是大于中轴值的,将左游标处的值与末尾的中轴值交换,就完成了第一次的分割,接下来,对中轴两边的数组段再次进行上述相同的处理。最终就得到了排序好的数组。快速排序用了分而治之的思想,把大问题分割为小问题,小问题的求解方式与大问题是一样的,只是规模小,最终小问题的解集合起来就是大问题的解。int partition(int原创 2020-09-01 09:56:56 · 302 阅读 · 0 评论 -
十大排序:插入排序
插入排序及希尔排序插入排序的思想:插入排序的思想有点类似摸扑克牌,首先抽取第一张,当做已经排好序,然后第二张,插入到已经排好序的扑克牌中,依次类推,第三张...直到最后一张。void insertSort(int arr[],int len){ for(int i=1;i<len;i++) { //类似于倒着的冒泡排序 for(int j=0;j<i && arr[j] < arr[j-1];j++)原创 2020-08-31 17:46:46 · 122 阅读 · 0 评论 -
十大简单排序:冒泡排序
冒泡排序思想:从第一个位置开始,依次比较大小,若前面的值大,则将前值与后值交换,经过一轮的比较后,最大值就排在了末尾。由此而来,比较N轮,N是数组元素个数,那么整个数组就有序了。void swap(int arr[],int i,int j){ int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;}void BubbleSort(int arr[],int len){ for(int i=0;i<len;原创 2020-08-29 09:28:33 · 362 阅读 · 0 评论 -
十大简单排序:选择排序
选择排序思想:遍历一次数组,选择一个最小值,然后放到第一个位置,第二次遍历数组,再找到一个最小值,放入第二个位置,继续遍历数组,直到尾部,就把数组排好了。void swap(int arr[],int i,int j){ int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;}void selectionSort(int arr[],int len){ for(int i=0;i<len-1;i++)原创 2020-08-28 19:28:12 · 96 阅读 · 0 评论 -
链表--深度拷贝一个带有随机指针的链表
本文介绍两种解法。解法1:利用一个mapListNode *copyRandomList(ListNode *head){ if(head == NULL || head->next == NULL) return head; //定义一个map<ListNode*,ListNode*> hashMap用于存储旧节点和新节点的映射关系 map<ListNode*,ListNode*> hashMap; ..原创 2020-08-27 18:09:03 · 115 阅读 · 0 评论 -
链表--Reorder List
重排序一个链表Given a singly linked list L:L0->L1->...->Ln-1->Lnreorder it to :L0->Ln->L1->Ln-1->L2->Ln-2->...You must do this in-place without altering the node's values.Example:Given 1->2->3->4->null , reorder i原创 2020-08-27 16:31:29 · 169 阅读 · 0 评论 -
链表--逆序遍历链表
逆序遍历链表模式识别:如果对靠前节点的处理必须在靠后节点之后,即倒序访问问题,则用recursion(递归),或者等效地,stack来解决。例题:逆序遍历链表void traverse(ListNode *head){ if(head == NULL) return; traverse(head->next); visit(head);}...原创 2020-08-26 10:44:44 · 416 阅读 · 0 评论 -
链表--Merge Two Sorted List
融合两个有序链表题目:Merge two sorted linked lists and return it as a new list.本文介绍两种方法.方法1:用递归ListNode *mergeTwoLists(ListNode *L1, ListNode *L2){ if(L1 == NULL) return L2; if(L2 == NULL) return L1; if(L1->val < L2->val)原创 2020-08-26 10:35:53 · 191 阅读 · 0 评论 -
链表--两个链表相加
两个链表相加题目:给定两个链表,链表中的每一个元素是一个整数,写一个函数返回一个新的链表,新链表是给定两个链表的和。例如:7->1->6 + 5->9->2 返回 2->1->9ListNode *sum(ListNode *L1, ListNode *L2){ if(L1 == NULL) return L2; if(L2 == NULL) return L1; int cn = 0; ListNode *d原创 2020-08-26 08:56:27 · 278 阅读 · 0 评论 -
链表--交换链表相邻的两个元素
交换链表相邻的两个元素题目:给定一个链表,交换每个相邻的元素,最后返回新的链表节点。ListNode *swapNodes(ListNode *head){ if(head == NULL || head->next == NULL) return head; ListNode *dummyNode = new ListNode(0); dummyNode->next = head; ListNode *tmpNode = dummy原创 2020-08-25 16:00:56 · 2757 阅读 · 0 评论 -
链表--两种方法逆序一个给定的链表
两种方法逆序一个给定的链表本文介绍两种实现链表逆序的方式,第一种是非递归的方式,第二种是递归的方式。方法一:非递归的方式ListNode *reverse(ListNode *head){ //为空或只有一个,直接返回即可 if(head == NULL || head->next == NULL) return head; ListNode *pre = NULL; while(head != NULL) {原创 2020-08-25 13:48:03 · 159 阅读 · 0 评论 -
链表--逆时针旋转一个链表
逆时针旋转一个链表题目:给定一个链表和一个位置k,k代表第几个元素。k位置之后的的元素逆时针旋转到前面,k位置之前的元素(包含k位置元素)旋转到后面。假定k小于链表的长度,如果k大于等于链表的长度,那么不对链表做改动。思路:先找到k位置处的节点,这个节点将是新链表的头,然后继续向后遍历链表找到尾部节点,使尾部节点指向原先链表的头节点。//因为要改变链表头,所以要传入头节点的引用void rotate(ListNode **head_ref,int k){ if(k <= 0)原创 2020-08-25 11:56:53 · 131 阅读 · 0 评论 -
链表--双指针--求链表倒数第k个元素
求链表倒数第k个元素思路:可以定义两个临时指针,一个快指针,一个慢指针,使快指针领先k步,然后慢指针再走,当快指针到末尾的时候,那么慢指针的位置就是倒数第k个指针了。ListNode *findKthtoLast(ListNode *head,int k){ if(head == NULL || k < 0) return head; ListNode *runner = head, *chaser = head; for(int i=0;i<原创 2020-08-25 11:02:38 · 203 阅读 · 0 评论 -
链表--Dummy Node小技巧(2)重排序一个给定链表
Dummy Node小技巧:重排序一个给定链表题目:给定一个链表和值x,重排序这个链表,使得小于x的值在大于等于x的值的前面。分析:可以考虑先把小于x的值组成一个链表,大于等于x的值组成一个链表,然后再合并这两个链表即可。那么这样的话,两个链表的头是不确定的,这个时候就自然要考虑用Dummy Node,由于是两个链表头,所以用两个Dummy Node。代码如下:ListNode *partition(ListNode *head,int val){ //为空或者只有一个,那么原创 2020-08-24 18:49:27 · 540 阅读 · 0 评论 -
链表--Dummy Node小技巧(1)删除有序链表中的所有重复节点
Dummy Node小技巧题目:给定一个有序链表,删除所有重复的节点,只留下原链表中出现一次的元素。例如:给定链表1->2->3->3->4->4->5 返回1->2->5 给定链表1->1->1->2->3 返回2->3ListNode *removeDuplicates(ListNode *head){ //链表为空或者只有一个元素,则返回head if(head ==...原创 2020-08-24 17:24:28 · 1640 阅读 · 1 评论 -
链表--从有序链表中删除重复元素
从有序链表中删除重复元素给定一个有序链表,删除所有重复的元素,使链表中的每个元素只出现一次。例如,给定链表1->1->2,返回1->2. 给定链表1->1->2->3->3,返回1->2->3.ListNode *removeDuplicate(ListNode *head){ //只有一个元素或者为空,直接返回头结点 if(head == NULL || head->next == NULL) ret.原创 2020-08-24 15:50:36 · 1430 阅读 · 0 评论 -
链表--以O(1)的时间复杂度删除当前节点
以O(1)的时间复杂度删除单链表的当前节点思路:将当前节点下一个节点的值赋值给当前节点,然后删除下一个节点。详细说明如下图所示:注意两点:1)这样操作虽然保持了原有节点顺序的值,但是破坏了节点对应的地址。2)如果要删除的节点是尾节点,还得从头遍历一遍,时间复杂度依然是O(n)。...原创 2020-08-24 15:28:40 · 303 阅读 · 0 评论