劍指offer題解(十一):c++&java
序列化二叉樹
題目描述
請實現兩個函式,分別用來序列化和反序列化二叉樹
- 對於序列化:使用前序遍歷,遞迴的將二叉樹的值轉化為字元,並且在每次二叉樹的結點不為空時,在轉化val所得的字元之後新增一個’ , ‘作為分割。對於空節點則以 ‘#’ 代替。
- 對於反序列化:按照前序順序,遞迴的使用字串中的字元建立一個二叉樹(特別注意:在遞迴時,遞迴函式的引數一定要是char ** ,這樣才能保證每次遞迴後指向字串的指標會隨著遞迴的進行而移動!!!)
java
public class Solution {
String Serialize(TreeNode root)
{
if (root==null)
return "";
StringBuilder sb = new StringBuilder();
Serialize2(root,sb);
return sb.toString();
}
void Serialize2(TreeNode root, StringBuilder sb)
{
if(root==null)
{
sb.append("#,");
return;
}
sb.append(root.val);
sb.append(',' );
Serialize2(root.left,sb);
Serialize2(root.right,sb);
}
int index = -1;
TreeNode Deserialize(String str)
{
if(str.length()==0)
return null;
String [] strs = str.split(",");
return Deserialize2(strs);
}
TreeNode Deserialize2(String[] strs)
{
index ++;
if(!strs[index].equals("#"))
{
TreeNode root = new TreeNode(0);
root.val = Integer.parseInt(strs[index]);
root.left = Deserialize2(strs);
root.right = Deserialize2(strs);
return root;
}
return null;
}
}
c++
class Solution {
public:
vector<int> buf;
void dfs1(TreeNode *root) {
if(!root) buf.push_back(0xFFFFFFFF);
else {
buf.push_back(root->val);
dfs1(root->left);
dfs1(root->right);
}
}
TreeNode* dfs2(int* &p) {
if(*p==0xFFFFFFFF) {
p++;
return NULL;
}
TreeNode* res=new TreeNode(*p);
p++;
res->left=dfs2(p);
res->right=dfs2(p);
return res;
}
char* Serialize(TreeNode *root) {
buf.clear();
dfs1(root);
int bufSize=buf.size();
int *res=new int[bufSize];
for(int i=0;i<bufSize;i++) res[i]=buf[i];
return (char*)res;
}
TreeNode* Deserialize(char *str) {
int *p=(int*)str;
return dfs2(p);
}
};
字串的排列
題目描述
輸入一個字串,按字典序打印出該字串中字元的所有排列。例如輸入字串 abc,則打印出由字元 a, b, c 所能排列出來的所有字串 abc, acb, bac, bca, cab 和 cba。
ps:輸入一個字串,長度不超過9(可能有字元重複),字元只包括大小寫字母。
解題思路
c++
class Solution {
public:
vector<string> Permutation(string str) {
//可以用遞迴來做
vector<string> array;
if(str.size()==0)
return array;
Permutation(array, str, 0);
sort(array.begin(), array.end());
return array;
}
void Permutation(vector<string> &array, string str, int begin)//遍歷第begin位的所有可能性
{
if(begin==str.size()-1)
array.push_back(str);
for(int i=begin; i<=str.size()-1;i++)
{
if(i!=begin && str[i]==str[begin])//有重複字元時,跳過
continue;
swap(str[i], str[begin]);//當i==begin時,也要遍歷其後面的所有字元;
//當i!=begin時,先交換,使第begin位取到不同的可能字元,再遍歷後面的字元
Permutation(array, str, begin+1);//遍歷其後面的所有字元;
swap(str[i], str[begin]);//為了防止重複的情況,還需要將begin處的元素重新換回來
/*舉例來說“abca”,為什麼使用了兩次swap函式
交換時是a與b交換,遍歷;
交換時是a與c交換,遍歷;(使用一次swap時,是b與c交換)
交換時是a與a不交換;
*/
}
}
};
java
import java.util.*;
public class Solution {
public ArrayList<String> Permutation(String str) {
ArrayList<String> result = new ArrayList<String>() ;
if(str==null || str.length()==0) { return result ; }
char[] chars = str.toCharArray() ;
TreeSet<String> temp = new TreeSet<>() ;
Permutation(chars, 0, temp);
result.addAll(temp) ;
return result ;
}
public void Permutation(char[] chars, int begin, TreeSet<String> result) {
if(chars==null || chars.length==0 || begin<0 || begin>chars.length-1) { return ; }
if(begin == chars.length-1) {
result.add(String.valueOf(chars)) ;
}else {
for(int i=begin ; i<=chars.length-1 ; i++) {
swap(chars, begin, i) ;
Permutation(chars, begin+1, result);
swap(chars, begin, i) ;
}
}
}
public void swap(char[] x, int a, int b) {
char t = x[a];
x[a] = x[b];
x[b] = t;
}
}
陣列中出現次數超過一半的數字
題目描述
陣列中有一個數字出現的次數超過陣列長度的一半,請找出這個數字。例如輸入一個長度為9的陣列{1,2,3,2,2,2,5,4,2}。由於數字2在陣列中出現了5次,超過陣列長度的一半,因此輸出2。如果不存在則輸出0。
解題思路
多數投票問題,可以利用 Boyer-Moore Majority Vote Algorithm 來解決這個問題,使得時間複雜度為 O(N)。
使用 cnt 來統計一個元素出現的次數,當遍歷到的元素和統計元素不相等時,令 cnt–。如果前面查找了 i 個元素,且 cnt == 0 ,說明前 i 個元素沒有 majority,或者有 majority,但是出現的次數少於 i / 2 ,因為如果多於 i / 2 的話 cnt 就一定不會為 0 。此時剩下的 n - i 個元素中,majority 的數目依然多於 (n - i) / 2,因此繼續查詢就能找出 majority。
java
public int MoreThanHalfNum_Solution(int[] nums) {
int majority = nums[0];
for (int i = 1, cnt = 1; i < nums.length; i++) {
cnt = nums[i] == majority ? cnt + 1 : cnt - 1;
if (cnt == 0) {
majority = nums[i];
cnt = 1;
}
}
int cnt = 0;
for (int val : nums)
if (val == majority)
cnt++;
return cnt > nums.length / 2 ? majority : 0;
}
c++
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers)
{
if(numbers.empty()) return 0;
// 遍歷每個元素,並記錄次數;若與前一個元素相同,則次數加1,否則次數減1
int result = numbers[0];
int times = 1; // 次數
for(int i=1;i<numbers.size();++i)
{
if(times == 0)
{
// 更新result的值為當前元素,並置次數為1
result = numbers[i];
times = 1;
}
else if(numbers[i] == result)
{
++times; // 相同則加1
}
else
{
--times; // 不同則減1
}
}
// 判斷result是否符合條件,即出現次數大於陣列長度的一半
times = 0;
for(int i=0;i<numbers.size();++i)
{
if(numbers[i] == result) ++times;
}
return (times > numbers.size()/2) ? result : 0;
}
};
最小的 K 個數
題目描述
輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。
python
先皮一下
import heapq
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
if k> len(tinput):
return []
return list(heapq.nsmallest(k,tinput))
用api也可以通過相應的測試
解題思路
快速選擇
複雜度:O(N) + O(1)
只有當允許修改陣列元素時才可以使用
快速排序的 partition() 方法,會返回一個整數 j 使得 a[l..j-1] 小於等於 a[j],且 a[j+1..h] 大於等於 a[j],此時 a[j] 就是陣列的第 j 大元素。可以利用這個特性找出陣列的第 K 個元素,這種找第 K 個元素的演算法稱為快速選擇演算法。
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k) {
ArrayList<Integer> ret = new ArrayList<>();
if (k > nums.length || k <= 0)
return ret;
int kthSmallest = findKthSmallest(nums, k - 1);
// findKthSmallest 會改變陣列,使得前 k 個數都是最小的 k 個數
for (int i = 0; i < k; i++)
ret.add(nums[i]);
return ret;
}
public int findKthSmallest(int[] nums, int k) {
int l = 0, h = nums.length - 1;
while (l < h) {
int j = partition(nums, l, h);
if (j == k)
break;
if (j > k)
h = j - 1;
else
l = j + 1;
}
return nums[k];
}
private int partition(int[] nums, int l, int h) {
// 切分元素
int parti = nums[l];
int i = l, j = h + 1;
while (true) {
while (i != h && nums[++i] < parti) ;
while (j != l && nums[--j] > parti) ;
if (i >= j)
break;
swap(nums, i, j);
}
swap(nums, l, j);
return j;
}
private void swap(int[] nums, int i, int j) {
int t = nums[i];
nums[i] = nums[j];
nums[j] = t;
}
最小堆的方法
複雜度:O(NlogK) + O(K)
特別適合處理海量資料
應該使用大頂堆來維護最小堆,而不能直接建立一個小頂堆並設定一個大小,企圖讓小頂堆中的元素都是最小元素。
維護一個大小為 K 的最小堆過程如下:在新增一個元素之後,如果大頂堆的大小大於 K,那麼需要將大頂堆的堆頂元素去除。
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k) {
if (k > nums.length || k <= 0)
return new ArrayList<>();
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1);
for (int num : nums) {
maxHeap.add(num);
if (maxHeap.size() > k)
maxHeap.poll();
}
ArrayList<Integer> ret = new ArrayList<>(maxHeap);
return ret;
}