1. 程式人生 > >leetCode解題報告5道題(九)

leetCode解題報告5道題(九)

down 指向 list represent i+1 報告 net efi lee

題目一:Combinations

Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.

For example,
If n = 4 and k = 2, a solution is:

[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

分析:

題意給我們一個數字n, 和一個數字k,讓我們求出從 1~~n中取出k個數所能得到的組合數

所以這個題目給我們的第一感覺就是用遞歸是最方便的了。咱試試遞歸的方法哈。假設讀者對遞歸方法有疑問,能夠看看我之前總結的一個遞歸算法的集合。

本文專註於<遞歸算法和分治思想>

AC代碼:

public class Solution {
    //終於結果
    private ArrayList<ArrayList<Integer>> arrays = new ArrayList<ArrayList<Integer>>();
    
    public ArrayList<ArrayList<Integer>> combine(int n, int k) {
        //把組合設想成僅僅能升序的話,能做開頭的數字僅僅有 1 ~ n-k+1 
        for (int i=1; i<=n-k+1; ++i){
            ArrayList<Integer> list = new ArrayList<Integer>();
            list.add(i);
            cal(list, i+1, n, k-1); //遞歸
        }
        return arrays;
    }
    public void cal(ArrayList<Integer> list, int start, int end, int k){
        //k==0表示這次list組合滿足條件了
        if (k == 0){
            //copy
            ArrayList<Integer> result = new ArrayList<Integer>(list);
            arrays.add(result);
        }
        //循環遞歸調用
        for (int i=start; i<=end; ++i){
            list.add(i);
            cal(list, i+1, end, k-1);
            list.remove(list.size()-1);
        }
    }
}

題目二:Search a 2D Matrix

Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:

  • Integers in each row are sorted from left to right.
  • The first integer of each row is greater than the last integer of the previous row.

For example,

Consider the following matrix:

[
  [1,   3,  5,  7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]

Given target = 3, return true.

分析:這道題目是劍指Offer上的老題目咯,可是解題的思路挺巧妙。本來不想把這題寫在博文裏的。後來想想或許有些同學沒看過劍指Offer, 後續由於這題而去看下這本挺不錯的書哈,於是把這題寫在博文裏了。並附上劍指offer的下載地址(http://download.csdn.net/detail/u011133213/7268315),這題便不做具體介紹。


AC代碼:(復雜度O(m+n))

public class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length;
        int n = matrix[0].length;
        int row = 0;
        int col = n - 1;
        while (m > row && col >= 0){
            if (target == matrix[row][col]){
                return true;
            }
            if (target > matrix[row][col]){
                row++;//往下一行搜索
            }else if (target < matrix[row][col]){
                col--;//往左一列搜索
            }
        }
        return false;
    }
}


題目三:Scramble String

Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively.

Below is one possible representation of s1 = "great":

    great
   /      gr    eat
 / \    /  g   r  e   at
           /           a   t

To scramble the string, we may choose any non-leaf node and swap its two children.

For example, if we choose the node "gr" and swap its two children, it produces a scrambled string "rgeat".

    rgeat
   /      rg    eat
 / \    /  r   g  e   at
           /           a   t

We say that "rgeat" is a scrambled string of "great".

Similarly, if we continue to swap the children of nodes "eat" and "at", it produces a scrambled string "rgtae".

    rgtae
   /      rg    tae
 / \    /  r   g  ta  e
       /       t   a

We say that "rgtae" is a scrambled string of "great".

Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1.

分析:

這道題目的題意相信大家應該都看得懂,僅僅是做起來的話有些蛋疼.

我一開始用暴力法TLE,之後用DP才幹夠的.

詳細看代碼:

暴力法TLE:

public class Solution {
    public boolean isScramble(String s1, String s2) {
        
        if (s1 == null || s2 == null)
            return false;
        if (s1.equals(s2))
            return true;
        int len1 = s1.length();   
        int len2 = s2.length();
        if (len1 != len2)
            return false;
        int[] hash = new int[26];
        for (int i=0; i<len1; ++i){
            hash[s1.charAt(i) - ‘a‘]++;
        }
        for (int i=0; i<len2; ++i){
            hash[s2.charAt(i) - ‘a‘]--;
        }
        for (int i=0; i<26; ++i){
            if (hash[i] != 0)
                return false;
        }
        
        for (int i=1; i<len1; ++i){
            boolean flags1 = (isScramble(s1.substring(0,i), s2.substring(0,i)) && isScramble(s1.substring(i,len1), s2.substring(i,len2)));
            boolean flags2 = (isScramble(s1.substring(0,i), s2.substring(len2-i,len2)) && isScramble(s1.substring(i,len1), s2.substring(0,len2-i)));
            if (flags1 && flags2){
                return true;
            }
        }
        return false;
    }
}

DP動態規劃:

設數組flags[len][len][len]來存儲每個狀態信息.

如flags[i][j][k] 表示s1字符串的第i個位置開始的k個字符和s2字符串的第j個位置開始的k個字符 是否滿足scramble string

滿足:flags[i][j][k] == true

不滿足: flags[i][j][k] == false

那麽題目所要的終於結果的值事實上就相當於是flags[0][0][len]的值了

那麽狀態轉移方程是什麽呢?

歸納總結法

假設k==1:

flags[i][j][1] 就相當於 s1的第i個位置起。取1個字符。

s2的第j個位置起,取1個字符。那假設要使Scramble String成立的話,那麽就僅僅能是這兩個字符相等了, s1.charAt(i) == s2.charAt(j)

因此 flags[i][j][1] = s1.charAt(i) == s2.charAt(j);


假設k==2:

flags[i][j][2] 就相當於 s1的第i個位置起,取2個字符。s2的第j個位置起,取2個字符。那假設要使Scramble String成立的話,那麽情況有下面兩種

一種是flags[i][j][1] && flags[i+1][j+1][1] (就是兩個位置的字符都相等 S1="TM" S2="TM")

一種是flags[i][j+1][1] && flags[i+1][j][1] (兩個位置的字符剛好對調位置 S1="TM" S2="MT")


假設k==n:

設個變量為x

flags[i][j][n] =( (flags[i][j][x] && flags[i+x][j+x][k-x]) [第一種情況]

|| (flags[i][j+k-x][x] && flags[i+x][j][k-x]) ); [另外一種情況]


AC代碼:

public class Solution {
    public boolean isScramble(String s1, String s2) {
        
        if (s1 == null || s2 == null)
            return false;
        if (s1.equals(s2))
            return true;
        int len1 = s1.length();   
        int len2 = s2.length();
        if (len1 != len2)
            return false;
        int len = len1;
        boolean[][][] flags= new boolean[len+1][len+1][len+1];
        for (int t=1; t<=len; ++t){
            for (int i=0; i<=len-t; ++i){
                for (int j=0; j<=len-t; ++j){
                    if (t == 1){
                        flags[i][j][t] = (s1.charAt(i) == s2.charAt(j));
                    }else{
                        for (int k=1; k<t; ++k){
                            if (flags[i][j][t] == true){
                                break;
                            }else{
                                if ((flags[i][j][k] && flags[i+k][j+k][t-k])
                                    || (flags[i][j+t-k][k] && flags[i+k][j][t-k])){
                                    flags[i][j][t] = true;
                                }
                            }
                        }
                    }
                }
            }
        }
        return flags[0][0][len];
    }
}


題目四:

Rotate List

Given a list, rotate the list to the right by k places, where k is non-negative.

For example:
Given 1->2->3->4->5->NULL and k = 2,
return 4->5->1->2->3->NULL.

分析:

題意要求我們依據所給出的k值,把從最後一個非空結點向前的k個結點移動到鏈表的開頭,又一次組成一個新的鏈表之後返回。

這道題目有點像經典的面試題“僅僅遍歷一次。怎樣找到鏈表倒數的第K個結點”。採用的是兩個指針不一樣的起始位置,一個指針從head開始出發,還有一個指針先讓它走K步。當第2個指針為Null的時候。則第一個指針所指向的就是倒數第K個的值。

同理:

這裏我們用兩個指針就能夠方便地搞定這個題目了,可是須要註意的是,這道題目

假設K大於了鏈表長度,比方K=3。Len=2的話。假設K步我們還沒走完就碰到了Null結點。那麽再從head開始走剩下的。直到K==0。


AC代碼:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode rotateRight(ListNode head, int n) {
        ListNode firstNode = head;//新鏈表的第一個結點
        ListNode kstepNode = head;//走了k步的指針
        ListNode preFirstNode = new ListNode(-1);//新鏈表第一個結點的前一個結點
        ListNode preKstepNode = new ListNode(-1);//k步指針的前一個結點
        
        if (head == null || n == 0){
            return head;
        }
        int k = n;
        
        //先走K步
        while (k != 0){
            //假設走到鏈表結束了k還不為0,那麽再回到head頭結點來繼續
            if (kstepNode == null){
                kstepNode = head;
            }
            kstepNode = kstepNode.next;
            k--;
        }
        //假設剛好走到鏈表結束。那麽就不用再處理了,當前的鏈表就滿足題意了
        if (kstepNode == null){
            return firstNode;
        }
        //處理兩個結點同一時候走,知道第二個指針走到Null,則第一個指針是倒數第K個結點
        while (kstepNode != null){
            preFirstNode = firstNode;
            firstNode = firstNode.next;
            
            preKstepNode = kstepNode;
            kstepNode = kstepNode.next;
        }
        preKstepNode.next = head;
        preFirstNode.next = null;
        
        return firstNode;
    }
}


題目五:Partition List

Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.

You should preserve the original relative order of the nodes in each of the two partitions.

For example,
Given 1->4->3->2->5->2 and x = 3,
return 1->2->2->4->3->5.

分析:

媽蛋,英文題目看著就是蛋疼。看了好久才理解它的題意:

題目要求我們說給出一個X的值,你要把全部的 小於X的值的結點放在全部大於或等於X的值的前面,關鍵這裏X又等於3。跟題目裏面給出的鏈表中當中一個結點的值一樣,迷惑了。

一旦題意明確了,剩下的就好辦了,竟然這種話,那我們僅僅須要先找出第一個 “大於或等於X值”的結點,並記錄下它的位置。

然後剩下的僅僅要遍歷一次鏈表,把小於X的插入到它的前面,大於或等於X 不變位置(由於我們這裏找到的是第一個“大於或等於X值”的結點)。


AC代碼:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode partition(ListNode head, int x) {
        ListNode firstNode = head;
        ListNode preFirstNode = new ListNode(-1);
        preFirstNode.next = firstNode;
        ListNode tempNode = head;
        ListNode pretempNode = new ListNode(-1);
        pretempNode.next = tempNode;
        ListNode preHead = new ListNode(-1);
        preHead.next = head;
        
        int index = 0;
        //find the first (>= x)‘s node
        while (firstNode != null){
            if (firstNode.val >= x){
                break;
            }else{
                preFirstNode = firstNode;
                firstNode = firstNode.next;
                index++;//記錄位置
            }
        }
        //假設第一個滿足條件的結點是頭結點
        if (firstNode == head){
            preHead = preFirstNode;
        }
        //取得當前下標,假設在第一個滿足條件的結點之前則不處理
        int p = 0;
        while (tempNode != null){
            if (tempNode != firstNode){
                //值小於x,而且在第一個滿足條件結點之後。
                if (tempNode.val < x && p > index){
                    /*做移動*/
                    pretempNode.next = tempNode.next;
                    tempNode.next = preFirstNode.next;
                    preFirstNode.next = tempNode;
                    preFirstNode = tempNode;
                    tempNode = pretempNode.next;
                    index++;
                    p++;
                    continue;
                }
            }
            pretempNode = tempNode;
            tempNode = tempNode.next;
            p++;
        }
        
        return preHead.next;
        
    }
}


leetCode解題報告5道題(九)