微软面试100题系列算法心得

微软100题系列地址

答案地址

谓之随笔,当是自己在练习此类算法的一些想法,一些心得,一些领悟,一些借鉴,当自引用之时,会附上相应的链接!

题:把二元查找树转变成排序的双向链表(树)

描述:输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。要求不能创建任何新的结点,只调整指针的指向。

思维过程[个人思维]:

1. 二元查找树是指在任何结点看来,它的左子树上的值要少于当前结点的值,而它的右子树上的值要大于当前结点的值,对于等于的值那就看自己的原则放左子树还是右子树。

2. 关于树的算法必须要能够想到一些关键词:遍历算法[前序遍历、中序遍历、后序遍历、层次遍历],算法复杂度O(logN)、O(1)、O(N)、O(NlogN),最常想到的是O(logN).递归算法及非递归算法[递归算法在树中应用最为广泛],各类树的特征及优势[常见的树有BST,AVL,B+-,堆,伸展树,线段树等],树的实现[静态实现,指针实现],树结点信息[保存对应的信息有时候非常重要]

3. 看原题例子,可知此题核心是:中序遍历,用描述性的思维来说,左树先来,然后就是自己,然后再是右树。中序遍历常用递归实现,非递归较为烦琐

4. 联系题中要求,要排成一个双向链表,那么首先想到是中序遍历所有结点,返回中序遍历的序列,再连接成双向链表,需要细微注意的地方则是:头结点无前序结点,尾结点无后序结点。

下面则是对应的代码[有误请指证,感谢!]

BST树的定义:

1 struct Node{
2     Node * left;
3     Node * right;
4     int value;
5     Node() :left(NULL), right(NULL){}
6 };

中序遍历:

 1 vector<Node*> convert_doubledLinked(Node * root)
 2 {
 3     if (root == NULL) return vector<Node*>();
 4     auto l = convert_doubledLinked(root->left);
 5     auto r = convert_doubledLinked(root->right);
 6     l.push_back(root);
 7     for (const auto & v : r)
 8         l.push_back(v);
 9
10     return l;
11 }

做链表并获取头结点:

 1 Node * get_head(vector<Node*> node_vector){
 2     if (node_vector.empty()) return NULL;
 3     Node * head = NULL;
 4     Node * prev = NULL;
 5
 6     for (auto & v : node_vector){
 7         if (head == NULL)
 8             head = v;
 9
10         if (head != v){
11             prev->right = v;
12             v->left = prev;
13         }
14
15         prev = v;
16         v->right = NULL;
17     }
18     head->left = NULL;
19     return head;
20 }

5. 当然我们可能更想不分成两部分做,想将其合并在一个递归里,如此首先要明白是当前结点在双向链表中的前序结点是哪一个,当前结点的后序结点又是哪一个,不难想到,前序结点是左子树中最后遍历的结点,描述性的说:在当前结点的左子树中一直往右走,走到尽头的那个点便是,后序结点是右子树中最先遍历的点,描述性的说:在当前结点的右子树中一直往左走,走到尽头的那个结点。我们有多种方法得到这两个值,譬如按定义去找,譬如把子树连接成双向链表,便可知一切。有时候想一想,只需要我们所需要的信息即可,希望左子树给我前序的结点,右子树给我后序的结点,思索着,我希望子树递归完后,它能给我最左边的结点与最右边的结点,如此递归函数附加两个引用返回值则可,当然,我也可以利用每个结点恰好能保存两个结点,而把这两个参数合并为一个。

代码如下:

 1 Node * convert_doubledLinked(Node * root, Node &data){
 2     if (root == NULL){
 3         data.left = NULL;
 4         data.right = NULL;
 5         return root;
 6     }
 7
 8     Node ta, tb;
 9
10     Node * l = convert_doubledLinked(root->left, ta);
11     Node * r = convert_doubledLinked(root->right, tb);
12
13     if (ta.right != NULL){
14         ta.right->right = root;
15         root->left = ta.right;
16     }
17     if (tb.left != NULL){
18         root->right = tb.left;
19         tb.left->left = root;
20     }
21
22     data.left = (ta.left == NULL) ? root : ta.left;
23     data.right= (tb.right == NULL)? root: tb.right;
24
25     return data.left;
26 }

时间: 2024-10-28 21:49:44

微软面试100题系列算法心得的相关文章

微软面试100题系列:字符串匹配算法,查找包含字符集的子串

觉得这题挺有意思,看了别的博客,找到了一种目前看来还不错的算法,为强化理解,就写了下来. 题目意思: 实现一个挺高级的字符匹配算法: 给一串字符串,要求找到符合要求的字符串,例如对于目的串:123,那么给定字符串中诸如1******3*****2,12******3这些形式的子串都要找出来,即子串中含有目的串的所有字符,输出所有符合条件的字符串,并求出最短子串 .类似于和谐系统. 例如:假如目的串为:"423",输入长字符串为:"4fsdfk2jfl3fd2jfksd3j4d

求子数组的最大和 【微软面试100题 第三题】

题目要求: 输入一个整型数组,数组里有整数也有负数.数组中连续的一个或多个整数组成一个子数组,每个子数组都有个一个和. 求所有子数组的和的最大值.要求时间复杂度为O(n). 例如:输入的数组为1,-2,3,10,-4,7,2,-5,和最大的子数组为3,10,-4,7,2,因此输出为该子数组的和18. 参考资料:剑指offer第31题.编程之美2.14. 题目分析: 依次读入数组值,采用两个临时变量 maxSum:保存当前数组元素之前的子数组的最大值: maxEndingHere:包含当前元素前一

C语言库函数实现 【微软面试100题 第八十三题】

题目要求 1.strcpy/memcpy/memmove: 2.memset函数: 3.字符串处理函数. 题目分析 1.接口定义: char * strcpy(char * dest, const char * src): void *memcpy(void *memTo, const void *memFrom, size_t size): void *memmove(void *dst,const void *src,size_t n):  函数区别: -->strcpy 和 memcpy主

调整数组顺序使奇数位于偶数前面 【微软面试100题 第五十四题】

题目要求: 输入一个整数数组,调整数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分. 要求时间复杂度为O(n). 参考资料:剑指offer第14题. 题目分析: 使用两个指针,pBegin和pEnd,pBegin从开头往后遍历,pEnd从结尾往前遍历,当pBegin遇到偶数和pEnd遇到奇数时,交换两个数,然后继续遍历,直到pBegin>pEnd,则结束. 代码实现: #include <iostream> using namespace std; void

C++异常安全的赋值运算符重载 【微软面试100题 第五十五题】

题目要求: 类CMyString的声明如下: class CMyString { public: CMyString(char *pData=NULL); CMyString(const CMyString &str); ~CMyString(void); CMyString &operator=(const CMyString &str); private: char *m_pData; }; 请实现其赋值运算符的重载函数,要求异常安全,即当对一个对象进行赋值时发生异常,对象的状

找出数组中两个只出现一次的数字 【微软面试100题 第六十一题】

题目要求: 一个整型数组里除了两个数字机之外,其他的数字都出现了两次. 请写程序找出这两个只出现一次的数字.要求时间复杂度O(N).空间复杂度O(1). 参考资料:剑指offer第40题. 题目分析: 已知: 1.两个相同的数字异或的结果为0,即a^a = 0. 2.两个不相同的数字异或的结果的二进制中某一位为1,则这两个数字的二进制中对应位一个为0,一个为1.如3^2 = 1,对于最低位的二进制,3的最低位二进制为1,2的最低位二进制位0,则结果1的最低位二进制肯定为1. 假设原数组中只出现一

在左移的递减数组中查找某数 【微软面试100题 第四十八题】

题目要求: 一个数组是由一个递减数列左移若干位形成的,比如{4,3,2,1,6,5}是由{6,5,4,3,2,1,}左移两位形成的,在这种数组中查找某个数. 题目分析: 方法1 逐个查找,遍历一遍数组,时间复杂度O(N). 方法2 二分查找,时间复杂度O(logN).具体分析见下图: 代码实现: #include <iostream> using namespace std; int Helper(int a[],int k,int s,int t); int FindKInArray(int

字符串匹配算法 【微软面试100题 第三十三题】

题目要求: 给一串很长字符串,要求找到符合要求的字符串. 例如目的串:123,则1*****3***2,12*****3这些都要找出来. 其实就是类似一些和谐系统...... 题目分析: 1.假如目的串为:"423",输入长字符串为:"4fsdfk2jfl3fd2jfksd3j4d4d4jkfd4jd3kdf2",则应该输出 "4fsdfk2jfl3","2jfksd3jld4","4jd3kdf2". 2

数组中超过出现次数一半的数字 【微软面试100题 第七十四题】

问题要求: 数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字. 参考资料:编程之美2.3 寻找发帖水王 问题分析: 方法1 对数组排序,然后顺次查找其中最多的: 方法2 对数组排序,最中间一个肯定为要找的数字,时间复杂度O(NlogN): 方法3 每次消去数组中两个不同的数,最后剩下的肯定为要找的数字,时间复杂度O(N). 方法3代码1如下,以下是图解代码1 代码实现: //代码1#include <stdio.h> int Find(int* ID, int N); int ma