依旧第四章:解决面试题的思路
- 例题23:从上到下打印二叉树,即按层次遍历,不是左右子树的操作所以不好用递归,则打印一层时,将下一层的节点依次进入队列,再出队,以先进先出的方法实现顺序打印。其本质就是广度优先遍历。
- 例题23的变法:从上到下打印,但每一层的节点放到一个数组中,最终返回一个二维数组。解法:在原来的基础上,增加三个变量来完成此功能。所在的层数,本层的节点剩余个数,下一层的节点总数。
- 例题24:判断一个数列是不是二叉搜索树的后序遍历
思路:后序遍历一个二叉搜索树,例如[5,7,6,9,11,10,8]。最后一个数字为根节点8,前面数字的前半部分[5,7,6]是左子树,后部分[9,11,10]是右子树,左子树小于根,右子树大于根,这就是规律。同样左右子树依旧是一样的规律,用递归判断。class Solution { public: bool verifyPostorder(vector<int>& postorder) { //是空为true,数组中有1个或者2个都可以组成二叉搜索树,也是递归的边界条件 if(postorder.empty() || postorder.size() == 1 || postorder.size() == 2) return true; int length = postorder.size(); int val_root = postorder[length-1]; int pos_of_divide = length - 1; //左右子树的分隔位置 bool flag = 0; for(int i = 0; i < length-1; ++i) { if(flag == 0 && postorder[i] > val_root) { flag = 1; //有一个比root大则flag=1, pos_of_divide = i; //定位分隔位置 } else if(flag == 1 && postorder[i] < val_root) //如果前面有大的,后面又出现小的,则肯定不是二叉搜索树,直接返回 return false; } //分割成左右子树两个数组,供递归使用 vector<int> left_tree; vector<int> right_tree; if(pos_of_divide != 0) left_tree = vector<int>(postorder.begin(), postorder.begin() + pos_of_divide); if(pos_of_divide != length-1) right_tree = vector<int>(postorder.begin() + pos_of_divide, postorder.end()-1); //递归判断左右子树是不是二叉搜索树,都是则返回true return verifyPostorder(left_tree) && verifyPostorder(right_tree); } }; ```
4.4 分解问题:各个击破的思想,将问题分割,以分治法实现算法,一般会采用递归实现
- 例题26:复杂链表的复制
在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
复制过程中,next的关系好复制,而random的方式不好复制,比如1节点指向7节点,1’节点要定位到新的7’节点处,如果常规方法需要O(n)复杂度,所有n个节点则需要O(n2)的复杂度。
考虑因为定位难的问题,怎么样通过现有节点关系能够巧妙定位到新的节点。我们在每个节点后面连接新节点7-7’-13-13’-11-11’-10-10’-1-1’,13’要random要指向7’,而7’又在7的后面,则可以很容易的定位。将所有的random复制完成后,再将这个长链按奇偶分为两部分,即是原来的和复制的。
代码则分为三部分,第一部分将a-b-c变成a-a-b-b-c-c。第二部分将新的节点的random复制完成。第三部分再将复制的新节点提取出来。 - 例题28:字符串的所有排列,输入"abc",输出{“abc” “acb” “bac” “bca” “cab” “cba”},输出所有排列方式,并不能有重复的。
思路:将问题分解,3个字母输入abc,则第一个字母三种选择。选择第一个字母为a,则情况变成"a"+("bc"的所有排列方式),"bc"的所有排列方式又可再往下分割,变成“b”+“c”的所有排列方式和“c”+“b”的所有排列方式。
可以看出是一层一层递归。即每次选择所有可能为第一个字母的元素,将其他元素递归。
还不能有重复,例如输入fxxx,只有fxxx,xfxx,xxfx,xxxf四种。则必须每次选择第一个元素时不可选择重复的。
class Solution { public: vector<string> permutation(string s) { vector<string>* p_stringVector = new vector<string>; PermutationRecursion(s,0,p_stringVector); return *p_stringVector; } void PermutationRecursion(string s, int start, vector<string>* p_stringVector){ if(start >= s.length()) // s为空时{ p_stringVector->push_back(""); return; } else if(start == s.length()-1){ //到最后一位,则为一种情况,加入到最后结果数组中-(边界条件) p_stringVector->push_back(s); return; } for(int i = start; i < s.length() ;i++){ if(isExist(s,start,i)) //判断是否重复 { mySwitch(s,start,i); PermutationRecursion(s, start+1, p_stringVector); //选定第一位,递归排列后面的字符 mySwitch(s,start,i); } } return; } void mySwitch(string *s, int a, int b) { char temp = (*s)[a]; (*s)[a] = (*s)[b]; (*s)[b] = temp; } bool isExist(string s, int start, int i){ for(; start < i; ++start){ if(s[start] == s[i]) return false; } return true; } };