剑指offer 刷题笔记–数组(1)
题目规定长度为n的数组中所有元素的大小都在0~n-1这个范围内,所以查找重复元素的时候要充分利用这个条件。也就是说,在遍历数组的时候,我们将每个元素放到对应的数组下标中(如:nums[0]为2,那么我们就将0下标的元素与2下标的元素交换,此时nums[2]就为2),当遍历到一个下标为i的元素x(即x=nums[i]),并且nums[x] == x时,我们就找到了这个重复元素x。
class Solution {
public int findRepeatNumber(int[] nums) {
int i = 0;
while(i < nums.length){
if(i == nums[i]){
i++;
continue;
}
if(nums[i] == nums[nums[i]]) return nums[i];
int tmp = nums[i];
nums[i] = nums[tmp];
nums[tmp] = tmp;
}
return -1;
}
}
查找数组中的数字,首选二分查找,时间复杂度O(logn)。
本题与寻常二分查找有所不同的是,在查找过程中遇到中间下标元素与边界元素相等的情况时,改为线性查找,此线性查找区间还是这一轮二分查找的区间[l,r],在这个区间中找到最小元素即可。
class Solution {
public int minArray(int[] numbers) {
int l = 0, r = numbers.length-1;
while(l < r){
int mid = (l + r)/2;
if(numbers[mid] > numbers[r]) l = mid + 1;
else if(numbers[mid] < numbers[r]) r = mid;
else{
//相等
int x = l;
for(int i = l;i < r; ++i){
if(numbers[i] < numbers[x]) x=i;
}
return numbers[x];
}
}
return numbers[l];
}
}
根据该二维数组的特点:每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。我们可以锁定该二维数组的右上角元素,每次将该元素与想要寻找的元素比较,如果matrix[i] [j]< target,就排除一行,如果matrix[i] [j] > target,就排除一列,以此可以很快找到想要的元素。
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if(matrix == null || matrix.length == 0) return false;
int i = 0,j = matrix[0].length-1;
while(i < matrix.length && j >= 0){
if(matrix[i][j] < target) i++;
else if(matrix[i][j] > target) j--;
else if(matrix[i][j] == target) return true;
}
return false;
}
}
用双指针遍历整个数组:左指针遇到偶数停下,右指针遇到奇数停下,交换左右指针指向的元素。重复这个过程直到两 指针相遇为止。
class Solution {
public int[] exchange(int[] nums) {
int l = 0, r = nums.length - 1;
while(l < r){
while(l < r && nums[l]%2 == 1) l++;
while(l < r && nums[r]%2 == 0) r--;
int tmp = nums[l];
nums[l] = nums[r];
nums[r] = tmp;
}
return nums;
}
}
关键在于设置左、右、上、下、边界来约束打印时的下标走向。每次打印完一行或者一列的时候,刷新边界值即可。当左右边界或者上下边界越界时打印结束。
class Solution {
public int[] spiralOrder(int[][] matrix) {
if(matrix.length == 0) return new int[0];
int l = 0;//左边界
int r = matrix[0].length-1;//右边界
int u = 0;//上边界
int n = matrix.length-1;//下边界
int[] res = new int[(r+1)*(n+1)];
int x = 0;
while(true){
for(int i = l;i<=r;++i){
res[x++] = matrix[u][i];
}
if(++u>n) break;
for(int i = u;i<=n;++i){
res[x++] = matrix[i][r];
}
if(--r<l) break;
for(int i = r;i>=l;--i){
res[x++] = matrix[n][i];
}
if(--n<u) break;
for(int i = n;i>=u;--i){
res[x++] = matrix[i][l];
}
if(++l>r) break;
}
return res;
}
}
只要求得最大的n位数就很简单:如最大的3位数为999=103-1
class Solution {
public int[] printNumbers(int n) {
int count = (int)Math.pow(10,n)-1;
int[] res = new int[count];
for(int i = 0;i<count;++i){
res[i] = i+1;
}
return res;
}
}
方法:深度优先搜索+剪枝 通过构造一个深度优先搜索的方法,传递的参数有五个:二维字符数组board,该单词的字符数组ch,二维数组小标i,j,单词字符数组下标k。
剪枝:遇到这条路不可能走通的情况下(此元素与目标字符不同,此元素已经被访问),立刻返回。
class Solution {
public boolean exist(char[][] board, String word) {
char[] ch = word.toCharArray();
for(int i = 0;i<board.length;++i){
for(int j = 0;j<board[0].length;++j){
if(dfs(board,ch,i,j,0))
return true;
}
}
return false;
}
public boolean dfs(char[][] board,char[] word,int i,int j,int k){
if(i<0 || j<0 || i>=board.length || j>=board[0].length || word[k] != board[i][j]) return false;
if(k == word.length-1) return true;
char c = board[i][j];
board[i][j] = '\0';
boolean res = dfs(board,word,i+1,j,k+1)||dfs(board,word,i-1,j,k+1)
||dfs(board,word,i,j+1,k+1) ||dfs(board,word,i,j-1,k+1);
board[i][j] = c;
return res;
}
}
代码参考LeetCode作者:Krahets