Next Right Pointers in Each Node

Problem 1

Given a binary tree

struct TreeLinkNode {
  TreeLinkNode *left;
  TreeLinkNode *right;
  TreeLinkNode *next;
}

Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL.

Initially, all next pointers are set to NULL.

Note:

You may only use constant extra space.
You may assume that it is a perfect binary tree (ie, all leaves are at the same level, and every parent has two children).
For example,
Given the following perfect binary tree,

     1
   /  \
  2    3
 / \  / \
4  5  6  7

After calling your function, the tree should look like:

     1 -> NULL
   /  \
  2 -> 3 -> NULL
 / \  / \
4->5->6->7 -> NULL

Solution 1

  • 将一个节点的左孩子链接到右孩子比较简单
  • 根不同的节点无法直接连接在一起,这时需要借用上一层的信息,如果不同根节点的孩子需要连在一起,那么他们的根节点一定是连接起来的
  • 所以只要有上一层的连接关系,就可以将下一层连接完成
设置pre和cur两个指针,指示父层
pre从跟开始,每次经过最左边的节点,cur从pre开始向右移动
每到一个新的cur,先设置cur.left.next = cur.right
如果cur有next节点,则需要设置cur.right.next = cur.next.left
一层循环完成,pre = pre.left,直到树叶
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void connect(TreeLinkNode *root) {
if (root == NULL) return;
TreeLinkNode *pre = root;
TreeLinkNode *cur = NULL;
while(pre->left) {
cur = pre;
while(cur) {
cur->left->next = cur->right;
if(cur->next) cur->right->next = cur->next->left;
cur = cur->next;
}
pre = pre->left;
}
}

Problem 2

把第一题中的perfect binary tree变成一般的二叉树,要求O(1)空间复杂度
比如:

     1
   /  \
  2    3
 / \    \
4   5    7

操作完成后:

     1 -> NULL
   /  \
  2 -> 3 -> NULL
 / \    \
4-> 5 -> 7 -> NULL

Solution 2

还是要利用父层的节点关系来帮助子层之间的连接,把父层和子层当成两个相关的list来处理
在二叉树为一般二叉树时,我们就需要考虑节点是否有左孩子,右孩子
我们引入三个指针now指示当前层最左节点。head和tail,分别表示子层上最左边的节点和当前最右的节点

1 从根节点开始,设置head = tail = null
2 如果now.left为空,跳过,否则:
    如果tail为空:head = tail = now.left. 表示当找到这一层的头
    否则:tail = tail.next = now.left. 把now.left接到tail上,tail移一位
3 对now.right的处理和now.left一样
4 处理完now的子树,将now向右移,now = now.left
5 如果now = null,那么此时这一层已经结束,now下移一层到下一层最左边,即now = head,此时要把head和tail都设置为null
6 否则,进入下一循环
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void connect(TreeLinkNode *root) {
TreeLinkNode *now, *tail, *head;

now = root;
head = tail = NULL;
while(now)
{
if (now->left)
if (tail) tail = tail->next =now->left;
else head = tail = now->left;
if (now->right)
if (tail) tail = tail->next =now->right;
else head = tail = now->right;
if(!(now = now->next)) // now没有后继,跳到下一行最左边的节点
{
now = head;
head = tail=NULL; // head和tail设置为null
}
}
}
分享到

Permutation

生成全排列

举例来说,要生成[1,2,3,4,5]的全排列可以按如下方式继续:
1,x,x,x,x
2,x,x,x
3,x,x,x
4,x,x,x
5,x,x,x
2,x,x,x,x
3,x,x,x,x
4,x,x,x,x
5,x,x,x,x

所以可以使用递归的方式进行,设置一个pos表示当前递归到达数组的深度
然后:
for i=pos; i=n,则将一个排列结果存储,return

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Solution {
public List<List<Integer>> permute(int[] num) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
permute(num,0,result);
return result;
}

public void permute(int[] num, int begin, List<List<Integer>> result){
if(begin>=num.length){
List<Integer> list = new ArrayList<Integer>();
for(int i=0;i<num.length;i++){
list.add(num[i]);
}
result.add(list);
return;
}
for(int i=begin;i<num.length;i++){
swap(begin,i,num);
permute(num,begin+1,result);
swap(begin,i,num);
}
}

public void swap (int x, int y,int[] num){
int temp = num[x];
num[x]=num[y];
num[y]=temp;
}
}
下一个排列

寻找下一排列的方法如下:

  • 从后向前搜索,找到第一个num[i]<num[i+1],此时,[i+1, n-1]序号的元素非递增
    • 如果i+1=n-1的话,直接将num逆序即可
  • 从[i+1, n-1]前向遍历,找到第一个小于num[i]的数num[k],将num[i]与num[k]互换,注意此时[i+1, n-1]序号的元素还是非递增
  • 将[i+1, n-1]序号的元素逆序
第k个排列

还拿[1,2,3,4,5]的全排列举例:
以1开头的排列有4!个,以2开头的排列有4!个
所以思路很明显

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Solution {
public String getPermutation(int n, int k) {
int pos = 0;
List<Integer> numbers = new ArrayList<>();
int[] factorial = new int[n+1];
StringBuilder sb = new StringBuilder();

// create an array of factorial lookup
int sum = 1;
factorial[0] = 1;
for(int i=1; i<=n; i++){
sum *= i;
factorial[i] = sum;
}
// factorial[] = {1, 1, 2, 6, 24, ... n!}
// create a list of numbers to get indices
for(int i=1; i<=n; i++){
numbers.add(i);
}
// numbers = {1, 2, 3, 4}

k--;
for(int i = 1; i <= n; i++){
int index = k/factorial[n-i];
sb.append(String.valueOf(numbers.get(index)));
numbers.remove(index);
k-=index*factorial[n-i];
}
return String.valueOf(sb);
}
}
分享到

Maximum Gap

Problem

Given an unsorted array, find the maximum difference between the successive elements in its sorted form.

Try to solve it in linear time/space.

Return 0 if the array contains less than 2 elements.

You may assume all elements in the array are non-negative integers and fit in the 32-bit signed integer range.

Solution

解题思路:由于需要在O(n)的时间复杂度完成,一般的比较排序都不行了,这里考虑使用Bucket sort方法,主要的思想是将元素分配到bucket中

  • 扫描一遍数组,得到最大、最小值max、min
  • 创建n-1个bucket,从min到max,长度向上取整
  • 将数组中除min、max之外的n-2个元素放入n-1个bucket中,所以必然有一个为空
  • 每个bucket维护一个最大最小值,其他值忽略,因为最大间距必然出现在bucket之间
  • 注意也要考虑min和max与临近数的gap
  • 如果设置n+1个bucket的话,就可以把min、max也加入,而寻找最大gap时就不需要单独考虑min、max了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Solution {
public int maximumGap(int[] num) {
if (num.length < 2) return 0;
int N=num.length,bucketSize=0,bucketNum=0,minElement=Integer.MAX_VALUE,maxElement=0;
// 确定最值
for (int i=0; i<N; i++) {
minElement = Math.min(minElement, num[i]);
maxElement = Math.max(maxElement, num[i]);
}
bucketSize = (int)Math.ceil(((double)(maxElement-minElement))/(N-1)); // bucket的大小
bucketNum = (int)Math.ceil(((double)(maxElement-minElement))/bucketSize); // bucket数量
int[] bucketMax = new int[bucketNum+1],bucketMin = new int[bucketNum+1];
for (int i=0; i<=bucketNum; i++) {
bucketMax[i]=Integer.MIN_VALUE;
bucketMin[i]=Integer.MAX_VALUE;
}
for (int i=0; i<N; i++) { //put elements in buckets
if (num[i]==minElement || num[i]==maxElement) // 不加入最值
continue;
int bucketId = (int) Math.ceil(((double)(num[i]-minElement))/bucketSize); // 计算bucket编号
bucketMax[bucketId] = Math.max(num[i], bucketMax[bucketId]);
bucketMin[bucketId] = Math.min(num[i], bucketMin[bucketId]);
}
int maxGap=0, temp=minElement; // temp设置为最小值,可以捕捉最小值和第二小值之间的gap
// 在bucket之间寻找最大gap
for (int i=1; i<=bucketNum; i++) {
if (bucketMin[i]==Integer.MAX_VALUE)
continue;
if (maxGap < bucketMin[i]-temp) {
maxGap = bucketMin[i]-temp;
}
temp = bucketMax[i];
}
maxGap = Math.max(maxGap, maxElement-temp); 捕捉最大和第二大值之间的gap
return maxGap;
}
}
分享到

Longest Palindromic Substring

Problem

Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.

Solution

本题的解法不止一种。

DP解法
  • 维护一个二维数组,p[i][j]表示子串substring(i,j+1)是否为回文
  • p[i][j] = s[i]==s[j] ? p[i+1][j-1] : false;
  • 时间复杂度O(n^2)

具体代码就不贴了

扩展式解法
  • 循环扫描每一位
  • 以当前为基准,向左右扩展寻找回文子串,要注意的是奇数、偶数长度子串的不同
  • 最坏情况的时间复杂度O(n*len),len为最长回文子串的长度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Solution {
private int lo, maxLen;

public String longestPalindrome(String s) {
int len = s.length();
if (len < 2)
return s;
for (int i = 0; i < len-1; i++) {
extendPalindrome(s, i, i); //assume odd length
extendPalindrome(s, i, i+1); //assume even length
}
return s.substring(lo, lo + maxLen);
}

private void extendPalindrome(String s, int j, int k) {
while (j >= 0 && k < s.length() && s.charAt(j) == s.charAt(k)) {
j--;
k++;
}
if (maxLen < k - j - 1) {
lo = j + 1;
maxLen = k - j - 1;
}
}}
Manacher算法

Manacher算法是上面解法思想的延伸,主要除去了一些不必要的比较。

首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一个特殊的符号。比如 abba 变成 #a#b#b#a#, aba变成 #a#b#a#。 为了进一步减少编码的复杂度,可以在字符串的开始加入另一个特殊字符,这样就不用特殊处理越界问题,比如$#a#b#a#

以字符串12212321为例,经过上一步,变成了 S[] = “$#1#2#2#1#2#3#2#1#”;

然后用一个数组 P[i] 来记录以字符S[i]为中心的最长回文子串向左/右扩张的长度(包括S[i]),比如S和P的对应关系:

1
2
3
S     #  1  #  2  #  2  #  1  #  2  #  3  #  2  #  1  #
P 1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1
P[i]-1正好是原字符串中回文串的总长度

下面计算P[i],该算法增加两个辅助变量id和mx,其中id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界

这个算法的关键点就在这里了:

当 mx - i > P[j] 的时候,以S[j]为中心的回文子串包含在以S[id]为中心的回文子串中,由于 i 和 j 对称,以S[i]为中心的回文子串必然包含在以S[id]为中心的回文子串中,所以必有 P[i] = P[j],见下图。

当 P[j] > mx - i 的时候,以S[j]为中心的回文子串不完全包含于以S[id]为中心的回文子串中,但是基于对称性可知,下图中两个绿框所包围的部分是相同的,也就是说以S[i]为中心的回文子串,其向右至少会扩张到mx的位置,也就是说 P[i] >= mx - i。至于mx之后的部分是否对称,就只能一个一个匹配了。

对于 mx <= i 的情况,无法对 P[i]做更多的假设,只能P[i] = 1,然后再去匹配了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
#include <string>
#include<algorithm>
using namespace std;
// 将字符串处理,比如abcd变成:$#a#b#c#d#.
// 这样可以将奇数长度的回文和偶数长度回文一起处理。
string preProcess(string & str) {
string ret="$#";
for (int i=0; i<str.length(); i++) {
ret += str[i];
ret += "#";
}
return ret;
}
int p[2000009]={0};
void getMaxLength(string str) {
// mostFar记录了i之前回文到达的最右坐标
// id是与之对应的中心坐标。 p记录的是以i为中心的回文半价,单个字母为1.
int id=0, mostFar=0, maxL=0;
p[0] = 0;
for (int i=1; i<str.length(); i++) {
if (mostFar > i) {
int j=2*id-i; // j and i are symmetric by id.
if (p[j] < mostFar-i) // extension needed
p[i] = p[j];
else
p[i] = mostFar-i; // extension needed
} else
p[i] = 1;
// extension
while ((i+p[i]<str.length()) && (i-p[i]>=0) && str.at(i+p[i]) == str.at(i-p[i]))
++p[i];
if (p[i]+i > mostFar) { // update mostFar and id.
mostFar = (p[i]+i);
id = i;
}
}
for (int i=0; i<str.length(); i++)
maxL = max(maxL, p[i]);
cout<<maxL-1<<endl;
}
int main() {
int n=0;
cin>>n;
string str="";
for (int i=0; i<n; i++) {
cin>>str;
if (str.length() < 2)
cout<<str.length()<<endl;
else {
str = preProcess(str);
getMaxLength(str);
}
}
return 0;
}
分享到

Largest Rectangle in Histogram

Problem

Given n non-negative integers representing the histogram’s bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.

示例图

Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].
The largest rectangle is shown in the shaded area, which has area = 10 unit.

For example,
Given heights = [2,1,5,6,2,3],
return 10.

Solution

解题思路是:每到一个柱子i,计算以柱子i为最矮柱子的矩形面积,所以需要知道i左侧第一个小于h[i]的序号left,和i右侧第一个小于h[i]的序号right。做法是从左向右遍历,维护一个栈,每个柱子都会入栈一次。当一个比栈顶柱子更小的柱子出现时,栈顶柱子出栈,此时计算以这个出栈的柱子为最矮柱子的矩形面积。此时,当前循环的序号为right,栈里之前的元素是left,算法具体思路如下:

  • 创建空的栈
  • 从第一个柱子开始——>最后一个柱子
    • 如果栈为空或者h[i]大于栈顶的柱子高度,则将i入栈
    • 如果h[i]小于栈顶的柱子高度,持续从栈顶移除元素直到栈顶对应的柱子的值大于h[i]为止。设被移除的柱子为h[tp],将h[tp]当作最矮的柱子并计算对应的面积,此时,left为栈中tp之前的元素,right是当前的i
  • 如果栈不为空,那么逐个移除其元素,并且按上面的步骤计算对应的面积

因为每个柱子仅入栈、出栈一次,所以算法的复杂度为O(n)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
int largestRectangleArea(vector<int> &height) {
vector<int> s;
int ret = 0;
height.push_back(0);
for (int i=0; i<height.size(); i++) {
while (s.size()>0 && height[s.back()]>=height[i]) {
int h = height[s.back()], sid=0;
s.pop_back();
if (s.size() == 0)
sid = 0;
else
sid = s.back()+1;
if (ret < h*(i-sid))
ret = h*(i-sid);
}
s.push_back(i);
}
return ret;
}
}

分享到

Simulated Annealing

爬山算法

爬山算法是一种简单的贪心搜索算法,该算法每次从当前解的临近解空间中选择一个最优解作为当前解,直到达到一个局部最优解。爬山算法实现很简单,其主要缺点是会陷入局部最优解,而不一定能搜索到全局最优解。

模拟退火(Simulated Annealing)

爬山法是完完全全的贪心法,每次都鼠目寸光的选择一个当前最优解,因此只能搜索到局部的最优值。模拟退火其实也是一种贪心算法,但是它的搜索过程引入了随机因素。模拟退火算法以一定的概率来接受一个比当前解要差的解,因此有可能会跳出这个局部的最优解,达到全局的最优解。以图1为例,模拟退火算法在搜索到局部最优解A后,会以一定的概率接受到E的移动。也许经过几次这样的不是局部最优的移动后会到达D点,于是就跳出了局部最大值A。

模拟退火算法描述:
  • 若移动后得到更优解,则总是接受该移动
  • 若移动后的解比当前解要差,则以一定的概率接受移动,而且这个概率随着时间推移逐渐降低(逐渐降低才能趋向稳定)

这里的“一定的概率”的计算参考了金属冶炼的退火过程,这也是模拟退火算法名称的由来。
根据热力学的原理,在温度为T时,出现能量差为dE的降温的概率为P(dE),表示为:

P(dE) = exp( dE/(kT) )

其中k是一个常数,exp表示自然指数,且dE小于0。这条公式说白了就是:温度越高,出现一次能量差为dE的降温的概率就越大;温度越低,则出现降温的概率就越小。又由于dE总是小于0(否则就不叫退火了),因此dE/kT 小于0 ,所以P(dE)的函数取值范围是(0,1).
随着温度T的降低,P(dE)会逐渐降低。
我们将一次向较差解的移动看做一次温度跳变过程,我们以概率P(dE)来接受这样的移动。

伪代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
* J(y):在状态y时的评价函数值
* Y(i):表示当前状态
* Y(i+1):表示新的状态
* r: 用于控制降温的快慢
* T: 系统的温度,系统初始应该要处于一个高温的状态
* T_min :温度的下限,若温度T达到T_min,则停止搜索

while( T > T_min ) {
dE = J( Y(i+1) ) - J( Y(i) ) ;
if ( dE >=0 ) //表达移动后得到更优解,则总是接受移动
Y(i+1) = Y(i) ; //接受从Y(i)到Y(i+1)的移动
else {
// 函数exp( dE/T )的取值范围是(0,1) ,dE/T越大,则exp( dE/T )也
if ( exp( dE/T ) > random( 0 , 1 ) )
Y(i+1) = Y(i) ; //接受从Y(i)到Y(i+1)的移动
}
T = r * T ; //降温退火 ,0 < r < 1 。r越大,降温越慢;r越小,降温越快
/*
* 若r过大,则搜索到全局最优解的可能会较高,但搜索的过程也就较长。若r过小,则搜索的过程会很快,但最终可能会达到一个局部最优值
*/
i++ ;
}
使用模拟退火算法解决旅行商问题

旅行商问题 ( TSP , Traveling Salesman Problem ) :有N个城市,要求从其中某个问题出发,唯一遍历所有城市,再回到出发的城市,求最短的路线。
旅行商问题属于所谓的NP完全问题,精确的解决TSP只能通过穷举所有的路径组合,其时间复杂度是O(N!) 。
使用模拟退火算法可以比较快的求出TSP的一条近似最优路径。(使用遗传算法也是可以的,我将在下一篇文章中介绍)模拟退火解决TSP的思路:

  1. 产生一条新的遍历路径P(i+1),计算路径P(i+1)的长度L( P(i+1) )
  2. 若L(P(i+1)) < L(P(i)),则接受P(i+1)为新的路径,否则以模拟退火的那个概率接受P(i+1) ,然后降温
  3. 重复步骤1,2直到满足退出条件

产生新的遍历路径的方法有很多,下面列举其中3种:

  • 随机选择2个节点,交换路径中的这2个节点的顺序。
  • 随机选择2个节点,将路径中这2个节点间的节点顺序逆转。
  • 随机选择3个节点m,n,k,然后将节点m与n间的节点移位到节点k后面。
分享到

Insert Interval

Problem

Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary).
You may assume that the intervals were initially sorted according to their start times.

Example 1:
Given intervals [1,3],[6,9], insert and merge [2,5] in as [1,5],[6,9].

Example 2:
Given [1,2],[3,5],[6,7],[8,10],[12,16], insert and merge [4,9] in as [1,2],[3,10],[12,16].

This is because the new interval [4,9] overlaps with [3,5],[6,7],[8,10].

Solution

题的意思比较直观,简洁的代码是最棒的

1
2
3
4
5
6
7
8
9
10
11
12
13
public List<Interval> insert(List<Interval> intervals, Interval newInterval) {
int i=0;
// 把能合并的区间之前的区间skip
while (i<intervals.size() && intervals.get(i).end<newInterval.start) i++;
// 把能合并的区间删除
while (i<intervals.size() && intervals.get(i).start<=newInterval.end) {
newInterval = new Interval(Math.min(intervals.get(i).start, newInterval.start), Math.max(intervals.get(i).end, newInterval.end));
intervals.remove(i);
}
// 添加合并完的集合
intervals.add(i,newInterval);
return intervals;
}
分享到

Container With Most Water

Problem

Given n non-negative integers a1, a2, …, an, where each represents a point at coordinate (i, ai).
n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0).
Find two lines, which together with x-axis forms a container, such that the container contains the most water.

Solution

思路如下:假如已经计算了i和j之间的最大值

  • 假如: height[i]<height[j],因为对所有的k属于[i+1, j-1],必然有m[i][j] 大于 m[i][k],所以++i
  • 反之:—j
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
int maxArea(vector<int> &height) {
int i=0, j=height.size()-1, maxArea=0;
while (i < j) {
maxArea = max(maxArea, (j-i)*min(height[i],height[j]));
if (height[i] <= height[j])
++i;
else
--j;
}
return maxArea;
}
}
分享到

Binary Tree Traverse

updated


前序遍历

根据前序遍历访问的顺序,优先访问根结点,然后再分别访问左孩子和右孩子。即对于任一结点,其可看做是根结点,因此可以直接访问,访问完之后,若其左孩子不为空,按相同规则访问它的左子树;当访问其左子树时,再访问它的右子树。因此其处理过程如下:
首先将根节点入栈,然后对于任一结点P:

  1. 弹出节点P,访问P
  2. 分别将节点P的右、左孩子入栈(如果有的话)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void preOrder(TreeNode *root) {    //非递归前序遍历
stack<TreeNode*> s;
TreeNode *p=root;
while (p || !s.empty()) {
if (p != NULL) { // 使劲向左
visit(p);
s.push(p);
p = p->left;
} else {
p = s.top();
s.pop();
p = p->right;
}
}
}
中序遍历

顺序:左、中、右

对于任一结点P,

  1. 不断找当前结点的左边的子孙,将不是NULL的节点入栈
  2. 否则,弹出一个节点,并访问它,将其右子树入栈
  3. 直到P为NULL并且栈为空则遍历结束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void inOrder(TreeNode *root) {  //非递归中序遍历
stack<TreeNode*> s;
TreeNode *p = root;
while (p != NULL || !s.empty()) {
if (p != NULL) { // 使劲向左
s.push(p);
p = p->left;
} else {
p = s.top();
s.pop();
visit(p);
p = p->right;
}
}
}
后序遍历

顺序:左、右、中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void postOrder(TreeNode* root) {
stack<TreeNode*> s;
TreeNode *p = root;
TreeNode *pre = NULL; // last visited
while (p != NULL || !s.empty()) {
if (p != NULL) { // 使劲向左
s.push(p);
p = p->left;
} else {
p = s.top();
// 右孩子为空或者刚刚访问过了,可以访问当前节点了
if (p->right == NULL || pre == p->right) {
s.pop();
visit(p);
pre = p;
p = NULL; // 不置空就重复访问了
} else { // 否则访问右孩子
p = p->right;
}
}
}
}

另一个方法
intuition:先遍历到的点总是后访问的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void postOrder(TreeNode *root) {  //非递归后序遍历
stack<TreeNode*> sTraverse, sVisit;
sTraverse.push(root);
while(!s.empty()) {
TreeNode * p = sTraverse.top();
sTraverse.pop();
sVisit.push(p);
if (p->left) sTraverse.push(p->left);
if (p->right) sTraverse.push(p->right);
}
while (!sVisit.empty()) {
visit(sVisit.top());
sVisit.pop();
}
}
分享到

Palindrome Partitioning II

Problem

Given a string s, partition s such that every substring of the partition is a palindrome.

Return the minimum cuts needed for a palindrome partitioning of s.

For example, given s = “aab”,
Return 1 since the palindrome partitioning [“aa”,”b”] could be produced using 1 cut.

Solution

这题明显是DP题,因为满足最优子结构性质。
刚开始,我使用了如下的表达式

1
num[i,j] = min(num[i,k]+num[k+1,j])

但是超时了,在例子很大的时候TLE,其复杂度达到: O(n^3)

下面介绍通过的代码思想:

  • 用num[i]数组记录字符串s的[0~i-1]子串需要的最小切割次数
  • 每到一个i,向两侧延伸寻找最长的回文子串,需要分别考虑回文子串的长度为奇数、偶数
  • 比如考虑在i时的奇数长度回文子串,长度/2=k,则num[i+k]=min(num[i-k-1]+1, num[i+k])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Solution {
public int minCut(String s) {
int[] num = new int[s.length()+1];
for (int i = 0; i <= s.length(); i++) num[i] = i-1;
for (int i = 0; i < s.length(); i++) {
// 奇数回文子串
for (int j=0; i-j>=0 && i+j<s.length() && s.charAt(i-j)==s.charAt(i+j); j++)
num[i+j+1] = Math.min(num[i+j+1], num[i-j]+1);
// 偶数回文子串
for (int j=0; i-j>=0 && i+j+1<s.length() && s.charAt(i-j)==s.charAt(i+j+1); j++)
num[i+j+2] = Math.min(num[i+j+2], num[i-j]+1);
}
return num[s.length()];
}
}
分享到