目录
1.valid-palindrome
题目:判断题目给出的字符串是不是回文,仅考虑字符串中的字母字符和数字字符,并且忽略大小写。例如:"A man, a plan, a canal: Panama"是回文,"race a car"不是回文。注意:你有没有考虑过字符串可能为空?这是面试时应该提出的一个好问题。针对这个问题,我们定义空字符串是回文
分析:判断回文字符串只要判断第一个字符和最后一个字符是否相等,再递归判断中间的字符串是否是回文即可。因为题目中要求只考虑字母和数字,因此需忽略一些无效字符。
public boolean isPalindrome(String s) {
if(s.length() == 0)
return true;
int low = 0,high = s.length() - 1;
while(low < high){
//忽略除数字和字母以外的字符
while(low < high && ! isvalid(s.charAt(low)))
low++;
while(low < high && ! isvalid(s.charAt(high)))
high--;
if(low < high && Character.toLowerCase(s.charAt(low)) != Character.toLowerCase(s.charAt(high)))
return false;
low++;high--;
}
return true;
}
private boolean isvalid(char c) {
if(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9')
return true;
return false;
}
2.restore-ip-addresses
题目:现在有一个只包含数字的字符串,将该字符串重新存储成IP地址的形式,返回所有可能的情况。例如:给出的字符串为"25525511135",返回["255.255.11.135", "255.255.111.35"]. (顺序没有关系)
分析:分治法。IP地址被分为四个部分,先只考虑第一部分,第一部分可能是一个数字、两个数字或者三个数字,再递归求解剩余的字符串的形式。当遍历到字符串的最后一个数字且list中有四个数字就得到了一个合法序列。
public ArrayList<String> restoreIpAddresses(String s) {
ArrayList<String> list = new ArrayList<>();
if(s.length() == 0)
return list;
ArrayList<Integer> cur = new ArrayList<>();
util(s,list,cur,0);
return list;
}
private void util(String s, ArrayList<String> list, ArrayList<Integer> cur, int index) {
if(cur.size() == 4 && index == s.length()){
list.add(cur.get(0) + "." + cur.get(1) + "." + cur.get(2) + "."+ cur.get(3) );
return;
}
if(cur.size() == 4 || index == s.length())
return;
int num = 0;
//确定第一个数字,可能是一位、两位、三位
for(int i = index;i < index + 3 && i < s.length();i++){
num = num * 10 + s.charAt(i) - '0';
if(num > 255) //剪枝
return;
cur.add(num);
util(s,list,cur,i+1);
cur.remove(cur.size() - 1);
if(num == 0 || num > 25)//剪枝,数字的前缀不能为0
break;
}
}
3.sqrtx
题目:实现函数 int sqrt(int x). 计算并返回x的平方根
分析:采用二分查找的思想,注意:不用mid*mid进行判断,可能会溢出!
public int sqrt(int x) {
if(x < 2)
return x;
int low = 1,high = x / 2;
int mid,last_mid = 0;
while(low <= high){
mid = (low + high) / 2;
if(x / mid > mid){//x > mid * mid 会溢出
low = mid + 1;
last_mid = mid;
}
else if(x / mid < mid)
high = mid -1;
else
return mid;
}
return last_mid;
}
4.powx-n
题目:请实现函数 pow(x, n).即,求x的n次方。
分析:见剑指offer面试题16 https://blog.csdn.net/Nibaby9/article/details/104126811
5.permutations
题目:给出一组数字,返回该组数字的所有排列。例如:[1,2,3]的所有排列如下,[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2], [3,2,1].
分析:分治法,先只固定第一个位置的数字,再递归求解剩下数字的排列。牛客网中默认输出需按照字典序输出,所以固定第一个元素时应采用插入法进行插入。
public ArrayList<ArrayList<Integer>> permute(int[] num) {
ArrayList<ArrayList<Integer>> result = new ArrayList<>();
if(num.length == 0)
return result;
permute(num,result,0);
return result;
}
private void permute(int[] num, ArrayList<ArrayList<Integer>> result, int index) {
if(index == num.length - 1){
ArrayList<Integer> list = new ArrayList<>();
for(int i : num)
list.add(i);
result.add(list);
}
for(int i = index;i < num.length;i++){ //依次将第i个元素放到index位置上
swap1(num,index,i);
permute(num,result,index + 1);
swap2(num,index,i);
}
}
//将第i个元素前移到第index位置上
private void swap1(int[] num, int index, int i) {
int temp = num[i];
for(int j = i;j > index;j--)
num[j] = num[j-1];
num[index] = temp;
}
//将第index个元素后移还原到第i个位置上
private void swap2(int[] num, int index, int i) {
int temp = num[index];
for(int j = index;j < i;j++)
num[j] = num[j+1];
num[i] = temp;
}
6.permutations-ii
题目:给出一组可能包含重复项的数字,返回该组数字的所有排列。例如:[1,1,2]的排列如下,[1,1,2],[1,2,1], [2,1,1].
分析:类似题见剑指offer面试题38 https://blog.csdn.net/Nibaby9/article/details/103822794
如果直接在上题的基础上进行去重,会时间复杂度太高而通过不了。这里采用另一种方式,将每个元素都作为第一个元素进行添加,对于已添加的元素进行已访问标记,再递归求解其它元素的全排列。由于数组中可能包含重复项,所以我们需对数组进行排序,当当前元素和前一个元素相同且前一个元素未被访问时进行剪枝。
public ArrayList<ArrayList<Integer>> permuteUnique(int[] num) {
ArrayList<ArrayList<Integer>> result = new ArrayList<>();
if(num.length == 0)
return result;
ArrayList<Integer> list = new ArrayList<>();
Arrays.sort(num);
boolean[] visit = new boolean[num.length];
permute(num,result,list,visit);
return result;
}
private void permute(int[] num, ArrayList<ArrayList<Integer>> result,ArrayList<Integer> list,boolean[] visit) {
if(list.size() == num.length){//递归出口
result.add(new ArrayList<>(list));
return;
}
for(int i = 0;i < num.length;i++){//按顺序依次将每个元素加入到list中
if(visit[i])
continue;
if(i > 0 && num[i] == num[i-1] && !visit[i-1])
continue;
list.add(num[i]);visit[i] = true;
permute(num,result,list,visit);
list.remove(list.size() - 1);visit[i] = false;
}
}
7.next-permutation
题目:实现函数next permutation(下一个排列)。将排列中的数字重新排列成字典序中的下一个更大的排列。如果不存在这样的排列,则将其排列为字典序最小的排列(升序排列)。需要使用原地算法来解决这个问题,不能申请额外的内存空间。下面有机组样例,左边是输入的数据,右边是输出的答案1,2,3→1,3,2;3,2,1→1,2,3;1,1,5→1,5,1。
分析:首先从后往前找到第一个满足num[i] < num[i+1]的i,这样num[i]与后面的数交换才可能是下一个更大的序列;第i个位置需存放比num[i]更大的最小的数,将这两个数交换后,不难发现num[i]之后的数是从大到小排列的,将其翻转后就是我们要求的下一个更大的排列。比如说1 2 3 7 6 5 1,3就是我们首先要找的num[i],而5就是比3更大的最小的数,交换后就是1 2 5 7 6 3 1,将5后面的数字翻转后为1 2 5 1 3 6 7就是我们要求的下一个更大的排列。
public void nextPermutation(int[] num) {
if(num.length < 2)
return;
int index = num.length - 2;
//从后往前找到第一个满足num[i] < num[i+1]的i,这样num[i]与后面的数交换才可能是之后的序列
while(index >= 0 && num[index] >= num[index+1])
index--;
if(index >= 0){
//找到比num[i]更大的最小的数
int j = index + 1;
while(j < num.length && num[j] > num[index])
j++;
swap(num,index,--j);
}
//交换后实际上num[i]后面的数会从大到小排列,将其翻转就可从小到大
int low = index + 1,high = num.length - 1;
while(low < high)
swap(num,low++,high--);
}
private void swap(int[] num, int index, int j) {
int temp = num[index];
num[index] = num[j];
num[j] = temp;
}
8.permutation-sequence
题目:集合[1,2,3,…,n]一共有n!种不同的排列,按字典序列出所有的排列并且给这些排列标上序号,我们就会得到以下的序列(以n=3为例)
- "123"
- "132"
- "213"
- "231"
- "312"
- "321"
现在给出n和k,请返回第k个排列。注意:n在1到9之间
分析:可以利用求全排列的方法进行暴力求解,但时间空间复杂度都比较大。这里采用另一种方式,每次只找开头的元素,每个元素开头的排列有n! / n种,也就是我们将n! 种元素转化为一个n行n! / n列的矩阵,第k个元素所在的行数记为row,而第row个未被访问的数字,就是我们要找的开头的元素。第k个元素所在的列记为col,又把问题转化成了再该行中找第col个序列,递归求解即可。
public String getPermutation(int n, int k) {
if(n == 0)
return "";
int sum = 1;
boolean[] visit = new boolean[n];
for(int i = 2;i <= n;i++)
sum *= i;
return util(n,k,sum,visit);
}
private String util(int n, int k, int sum, boolean[] visit) {
if(n == 0)//递归出口
return "";
sum = sum / n;//相当于矩阵的列数,即相同开头数字有多少种
int row = (k - 1) / sum + 1,col = (k - 1) % sum + 1;
int i = 0;
while(row > 0){//第row个未被访问的数字就是要找的第一个数字
if(!visit[i])
row--;
i++;
}
visit[i-1] = true;
return String.valueOf(i) + util(n-1,col,sum,visit);
}