- 输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
解题思路
刚看题目的时候,可能会觉得这个问题很复杂,不能一下子想出解决方案。那我们就要学会把复杂的问题分解成小问题。我们求整个字符串的排列,其实可以看成两步:
第一步求所有可能出现在第一个位置的字符(即把第一个字符和后面的所有字符交换[相同字符不交换]);
第二步固定第一个字符,求后面所有字符的排列。这时候又可以把后面的所有字符拆成两部分(第一个字符以及剩下的所有字符),依此类推。这样,我们就可以用递归的方法来解决。
public class Solution {
ArrayList<String> res = new ArrayList<String>();
public ArrayList<String> Permutation(String str) {
if(str == null)
return res;
PermutationHelper(str.toCharArray(), 0);
Collections.sort(res);
return res;
}
public void PermutationHelper(char[] str, int i){
if(i == str.length - 1){
res.add(String.valueOf(str));
}else{
for(int j = i; j < str.length; j++){
if(j!=i && str[i] == str[j])
continue;
swap(str, i, j);
PermutationHelper(str, i+1);
swap(str, i, j);
}
}
}
public void swap(char[] str, int i, int j) {
char temp = str[i];
str[i] = str[j];
str[j] = temp;
}
}
- 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
解题思路
三种解法:
法1:借助hashmap存储数组中每个数出现的次数,最后看是否有数字出现次数超过数组长度的一半;
法2:排序。数组排序后,如果某个数字出现次数超过数组的长度的一半,则一定会数组中间的位置。所以我们取出排序后中间位置的数,统计一下它的出现次数是否大于数组长度的一半;
法3:某个数字出现的次数大于数组长度的一半,意思就是它出现的次数比其他所有数字出现的次数和还要多。因此我们可以在遍历数组的时候记录两个值:1. 数组中的数字;2. 次数。遍历下一个数字时,若它与之前保存的数字相同,则次数加1,否则次数减1;若次数为0,则保存下一个数字,并将次数置为1。遍历结束后,所保存的数字即为所求。最后再判断它是否符合条件。
方法1:
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
int length = array.length;
for(int i=0; i<length; i++){
if(!map.containsKey(array[i]))
map.put(array[i], 1);
else
map.put(array[i], map.get(array[i])+1);
}
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if(entry.getValue()*2>length)
return entry.getKey();
}
return 0;
}
}
方法2:
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
Arrays.sort(array);
int half = array.length/2;
int count = 0;
for(int i=0; i<array.length; i++){
if(array[i] == array[half])
count ++;
}
if(count > half)
return array[half];
else
return 0;
}
}
方法3
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
int res = array[0], count = 1;
for(int i=1; i<array.length; i++){
if(array[i] == res)
count++;
else{
count--;
}
if(count == 0){
res = array[i];
count = 1;
}
}
count = 0;
for(int i=0; i<array.length; i++){
if(array[i] == res)
count++;
}
return count > array.length/2 ? res : 0;
}
}
- 输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
两种方法:
法1:先对数组排序,然后取出前k个
法2:利用最大堆保存这k个数,每次只和堆顶比,如果比堆顶小,删除堆顶,新数入堆。
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> res = new ArrayList<Integer>();
if(input == null || k ==0 || k > input.length)
return res;
Arrays.sort(input);
for(int i=0; i<k; i++)
res.add(input[i]);
return res;
}
}
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> res = new ArrayList<Integer>();
if(input == null || k ==0 || k > input.length)
return res;
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k, new Comparator<Integer>() {
public int compare(Integer e1, Integer e2) {
return e2 - e1;
}
});
for(int i=0; i<input.length; i++){
if(maxHeap.size() != k)
maxHeap.offer(input[i]);
else{
if(maxHeap.peek() > input[i]){
maxHeap.poll();
maxHeap.offer(input[i]);
}
}
}
for(Integer i: maxHeap){
res.add(i);
}
return res;
}
}
- HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?(子向量的长度至少是1)
解题思路
对于一个数组中的一个数x,若是x的左边的数加起来非负,那么加上x能使得值变大,这样我们认为x之前的数的和对整体和是有贡献的。如果前几项加起来是负数,则认为有害于总和。
我们用cur记录当前值, 用max记录最大值,如果cur<0,则舍弃之前的数,让cur等于当前的数字,否则,cur = cur+当前的数字。若cur和大于max更新max。
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
if(array.length == 0)
return 0;
int cur = array[0], max = array[0];
for(int i=1; i<array.length; i++){
cur = cur > 0 ? cur + array[i] : array[i];
if(max < cur)
max = cur;
}
return max;
}
}
- 题目描述
求出113的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)
解题思路
三种解法:
法一:依次遍历每个数,判断每个数里面是否包含1
法二:同法一,将数字转成字符串,直接判断
法三:归纳法。
归纳法:
设N = abcde ,其中abcde分别为十进制中各位上的数字。
如果要计算百位上1出现的次数,它要受到3方面的影响:百位上的数字,百位以下(低位)的数字,百位以上(高位)的数字。
① 如果百位上数字为0,百位上可能出现1的次数由更高位决定。比如:12013,则可以知道百位出现1的情况可能是:100199,11001199,21002199,,…,1110011199,一共1200个。可以看出是由更高位数字(12)决定,并且等于更高位数字(12)乘以 当前位数(100)。
② 如果百位上数字为1,百位上可能出现1的次数不仅受更高位影响还受低位影响。比如:12113,则可以知道百位受高位影响出现的情况是:100199,11001199,21002199,,….,1110011199,一共1200个。和上面情况一样,并且等于更高位数字(12)乘以 当前位数(100)。但同时它还受低位影响,百位出现1的情况是:12100~12113,一共114个,等于低位数字(113)+1。
③ 如果百位上数字大于1(29),则百位上出现1的情况仅由更高位决定,比如12213,则百位出现1的情况是:100199,11001199,21002199,…,1110011199,1210012199,一共有1300个,并且等于更高位数字+1(12+1)乘以当前位数(100)。
方法1:
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int res = 0;
for(int i = 1; i <= n; i++)
res += number1(i);
return res;
}
public int number1(int n){
int res = 0;
while(n>0){
if(n % 10 == 1)
res++;
n /= 10;
}
return res;
}
}
法二:
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int res = 0;
StringBuffer s = new StringBuffer();
for(int i = 1; i<=n; i++){
s.append(i);
}
String str = s.toString();
for(int i=0; i<str.length(); i++){
if(str.charAt(i) == '1')
res++;
}
return res;
}
}
法三:
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int res = 0;
int cur = 0, before = 0, after = 0;
int i = 1;
while(i<=n){
before = n/(i*10);
cur = (n/i)%10;
after = n - n/i*i;
if(cur == 0){
// 如果为0,出现1的次数由高位决定,等于高位数字 * 当前位数
res += before * i;
}else if(cur == 1){
// 如果为1, 出现1的次数由高位和低位决定,高位*当前位+低位+1
res += before * i + after + 1;
}else{
// 如果大于1, 出现1的次数由高位决定,(高位数字+1)* 当前位数
res += (before + 1) * i;
}
i *= 10;
}
return res;
}
}