首页 > 代码库 > leetCode解题报告5道题(九)
leetCode解题报告5道题(九)
题目一: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]) ); [第二种情况]
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; } }