java数组算法题

1.给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。  
输入[0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。

思路解析:利用slow=0,fast=1,分别指向数组的第0,1号元素,if(他们相同时),{fast++},else{直到不同时,将slow的下一个元素替换为fast的元素,slow++,fast++}

public static int removeDuplicates(int[] nums) {
	int slow = 0;
	for (int fast = 1; fast < nums.length; fast++) {
		if (nums[slow] != nums[fast]) {
			nums[slow + 1] = nums[fast];
			slow++;
		}
	}
	return slow + 1;
}

2.给定一个数组,将数组中的元素向右移动 个位置,其中 是非负数。

输入: 
[1,2,3,4,5,6,7]
 和 k = 3
输出: 
[5,6,7,1,2,3,4]

说明:
.尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
.要求使用空间复杂度为 O(1) 的原地算法。

思路解析:元素后移k次就行了,另一种是利用翻转,任何数组翻转3次即可,还有一种思路新奇

/**
     * 翻转
     * 时间复杂度:O(n)
     * 空间复杂度:O(1)
     */
    public void rotate_2(int[] nums, int k) {
        int n = nums.length;
        k %= n;
        reverse(nums, 0, n - 1);
        reverse(nums, 0, k - 1);
        reverse(nums, k, n - 1);
    }


    private void reverse(int[] nums, int start, int end) {
        while (start < end) {
            int temp = nums[start];
            nums[start++] = nums[end];
            nums[end--] = temp;
        }
    }

    /**
     * 循环交换
     * 时间复杂度:O(n^2/k)
     * 空间复杂度:O(1)
     */
    public void rotate_3(int[] nums, int k) {
        int n = nums.length;
        k %= n;
        // 第一次交换完毕后,前 k 位数字位置正确,后 n-k 位数字中最后 k 位数字顺序错误,继续交换
        for (int start = 0; start < nums.length && k != 0; n -= k, start += k, k %= n) {
            for (int i = 0; i < k; i++) {
                swap(nums, start + i, nums.length - k + i);
            }
        }
    }

 

3.给定一个整数数组,判断是否存在重复元素。

如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。

输入: [1,2,3,1]
输出: true

输入: [1,2,3,4]
输出: false

思路解析:我做的是排序然后比较,还有set也比较容易想到,以及map的containKey方法

//Arrays.sort()
public boolean containsDuplicate(int[] nums) {
    Arrays.sort(nums);
    for(int i=0;i<nums.length-1;i++){
        if(nums[i]==nums[i+1]){
            return true;
        }
    }
    return false;
}
//if(!set.add())
public boolean containsDuplicate(int[] nums) {
	int len = nums.length;
	HashSet<Integer> set = new HashSet<>();
	for(int i=0;i<len;i++) {
		if(!set.add(nums[i]))return true;
	}
	return false;
}

//map.containsKey
public boolean containsDuplicate(int[] nums) {
    HashMap<Integer,Integer>map = new HashMap<>();
    for(int i=0;i<nums.length;i++){
        if(map.containsKey(nums[i])) return true;
        map.put(nums[i],0);
    }
    return false;
}
//Arrays.stream()
public boolean containsDuplicate(int[] nums) {
    Set<Integer> set = new HashSet<>();
    Arrays.stream(nums).forEach(set::add);
    return nums.length != set.size();
}

4.给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

输入: [2,2,1]
输出: 1

思路解析:运用" ^ "运算符,异或的运算方法是一个二进制运算: 1^1=0 ;0^0=0 ;1^0=1 ;0^1=1 

public int singleNumber(int[] nums) {
    int x=0;
    for(int i=0;i<nums.length;i++){
        x=x^nums[i];
    }
    return x;
}


5.给定两个数组,编写一个函数来计算它们的交集。

输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2,2]
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [4,9]

说明:
. 输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
. 我们可以不考虑输出结果的顺序。

思路解析:排序后利用双指针同时检索两个数组,取出相同的值

public int[] intersect(int[] nums1, int[] nums2) {
	Arrays.sort(nums1);
	Arrays.sort(nums2);
	List<Integer> list = new ArrayList<>();
	for (int i = 0, j = 0; i < nums1.length && j < nums2.length; ) {
		if (nums1[i] < nums2[j]) {
			i++;
		} else if (nums1[i] > nums2[j]) {
			j++;
		} else {
			list.add(nums1[i]);
			i++;
			j++;
		}
	}
	int[] res = new int[list.size()];
	for (int i = 0; i < list.size(); i++) {
		res[i] = list.get(i);
	}
	return res;
}

 

6.给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。你可以假设除了整数 0 之外,这个整数不会以零开头。

输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。

思路解析:如果是9,就相当于该位为0,前一位加1,直到第一位判定是否需要扩容

public int[] plusOne(int[] digits) {
	int len = digits.length;
	for(int i=len-1;i>=0;i--){
		if(digits[i]+1<10){
			digits[i]++;
			return digits;
		}else{
			digits[i]=0;
		}
	}
	int[] res=new int[len+1];
	res[0]=1;
	return res;
}

 

7.给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

输入: 
[0,1,0,3,12]
输出: 
[1,3,12,0,0]

说明:
. 必须在原数组上操作,不能拷贝额外的数组。
. 尽量减少操作次数。

思路解析:其他元素前移占据0的位置,然后再来补0,还有一种就是利用双指针交换后直接填充0,这里的if(i!=j)是为了防止找到0之前i,j相同

public void moveZeroes(int[] nums) {
	int i = 0, j = 0;
	while (j < nums.length) {
		if (nums[j] != 0) {
			nums[i++] = nums[j];
		}
		j++;
	}
	while (i < nums.length) nums[i++] = 0;
}
//
public void moveZeroes(int[] nums) {
	int j=0;
	for(int i=0;i<nums.length;i++){
		if(nums[i]!=0){
			nums[j]=nums[i];
			if(i!=j) nums[i]=0;
			j++;
		}
	}
}

 

8.给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

思路解析:双for暴力破解,最优解是一遍哈希表

public int[] twoSum(int[] nums, int target) {
	for(int i=0;i<nums.length-1;i++){
		for(int j=i+1;j<nums.length;j++){
			if(nums[i]+nums[j]==target){
				int[] a=new int[2];
				a[0]=i;
				a[1]=j;
				return a;
			}
		}
	}
	return null;
}
//优解
public int[] twoSum(int[] nums, int target) {
	Map<Integer,Integer> map=new HashMap<>();
	for(int i=0;i<nums.length;i++){
		int diff=target-nums[i];
		if(map.containsKey(diff)){
			return new int[] {map.get(diff),i};
		}
		map.put(nums[i],i);
	}
	return null;
}

 

9.判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

上图是一个部分填充的有效的数独。

数独部分空格内已填入了数字,空白格用 '.' 表示。

输入:
[
  ["5","3",".",".","7",".",".",".","."],
  ["6",".",".","1","9","5",".",".","."],
  [".","9","8",".",".",".",".","6","."],
  ["8",".",".",".","6",".",".",".","3"],
  ["4",".",".","8",".","3",".",".","1"],
  ["7",".",".",".","2",".",".",".","6"],
  [".","6",".",".",".",".","2","8","."],
  [".",".",".","4","1","9",".",".","5"],
  [".",".",".",".","8",".",".","7","9"]
]
输出: true

输入:
[
  ["8","3",".",".","7",".",".",".","."],
  ["6",".",".","1","9","5",".",".","."],
  [".","9","8",".",".",".",".","6","."],
  ["8",".",".",".","6",".",".",".","3"],
  ["4",".",".","8",".","3",".",".","1"],
  ["7",".",".",".","2",".",".",".","6"],
  [".","6",".",".",".",".","2","8","."],
  [".",".",".","4","1","9",".",".","5"],
  [".",".",".",".","8",".",".","7","9"]
]
输出: false

解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。

说明:
. 一个有效的数独(部分已被填充)不一定是可解的。
. 只需要根据以上规则,验证已经填入的数字是否有效即可。
. 给定数独序列只包含数字 1-9 和字符 '.' 。
. 给定数独永远是 9x9 形式的。

思路解析:用的boolean数组,但却像是map的思想,num作为每行的元素值,再用3个数组分别行,列与3x3的情况,boolean[9][10]长度为10因为不用0,当然这种方式不好想到。下面的暴力破解法写的比较优雅,也很好理解,k=i+1;总是从下一行开始与当前元素比较

public boolean isValidSudoku(char[][] board) {
       // 记录某行,某位数字是否已经被摆放
	boolean[][] row = new boolean[9][10];
        // 记录某列,某位数字是否已经被摆放
	boolean[][] col = new boolean[9][10];
        // 记录某 3x3 宫格内,某位数字是否已经被摆放
	boolean[][] block = new boolean[9][10];
	for (int i = 0; i < 9; i++) {
		for (int j = 0; j < 9; j++) {
			if (board[i][j] != '.') {
				int num = board[i][j] - '0';
				if (row[i][num] || col[j][num] || block[i / 3 * 3 + j / 3][num]) {
					return false;
				} else {
					row[i][num] = true;
					col[j][num] = true;
					block[i / 3 * 3 + j / 3][num] = true;
				}
			}
		}
	}
	return true;
}
//暴力破解
public static boolean isValidSudoku(char[][] board) {
	for(int i = 0; i < 9; i++){
		for(int j = 0; j < 9; j++){
			if(board[i][j] == '.')continue;
			for(int k = 8; k > j; k--){
				if(board[i][j] == board[i][k])
					return false;
			}
			for(int k = 8; k > i; k--){
				if(board[i][j] == board[k][j])
					return false;
			}
			for(int k = i + 1; k % 3 != 0; k ++){
				for(int h = j / 3 * 3;h < j / 3 * 3  + 3; h ++ ){
					if(board[i][j] == board[k][h]) 
						return false;
				}
			}
		}
	}
	return true;
}

 

10. 给定一个 × n 的二维矩阵表示一个图像。将图像顺时针旋转 90 度。

说明:你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。

给定 matrix = 
[
  [1,2,3],
  [4,5,6],
  [7,8,9]
],

原地旋转输入矩阵,使其变为:
[
  [7,4,1],
  [8,5,2],
  [9,6,3]
]

给定 matrix =
[
  [ 5, 1, 9,11],
  [ 2, 4, 8,10],
  [13, 3, 6, 7],
  [15,14,12,16]
], 

原地旋转输入矩阵,使其变为:
[
  [15,13, 2, 5],
  [14, 3, 4, 1],
  [12, 6, 8, 9],
  [16, 7,10,11]
]

思路解析:左下右上连线,元素对换后再镜像对称,这种方法化繁为简。

public void rotate(int[][] matrix) {
	int len=matrix[0].length;
	int mid=len/2;
	for(int i=1;i<len;i++){
		for(int j=0;j<i;j++){
			int temp=matrix[i][j];
			matrix[i][j]=matrix[j][i];
			matrix[j][i]=temp;
		}
	}
	for(int i=0;i<len;i++){
		for(int j=0;j<mid;j++){
			int temp=matrix[i][j];
			matrix[i][j]=matrix[i][len-j-1];
			matrix[i][len-j-1]=temp;
		}
	}
}
//暴力
public void rotate(int[][] matrix) {
	int len = matrix.length;
	for (int i = 0; i < len / 2; i++) {
		int start = i;
		int end = len - i - 1;
		for (int j = 0; j < end - start; j++) {
			int temp = matrix[start][start + j];
			matrix[start][start + j] = matrix[end - j][start];
			matrix[end - j][start] = matrix[end][end - j];
			matrix[end][end - j] = matrix[start + j][end];
			matrix[start + j][end] = temp;
		}
	}      
}

 11.给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

思路解析:排序,然后定义前后指针指向i+1与length,i为遍历的索引,内层循环当while(l<r)判断if (nums[l] + nums[r] == sum) ,符合条件的存入,当左边大时l++,否则r--,可以跳过重复元素。

public List<List<Integer>> threeSum(int[] nums) {
    Arrays.sort(nums);
    List<List<Integer>> ls = new ArrayList<>();
    for (int i = 0; i < nums.length - 2; i++) {
        if (i == 0 || (i > 0 && nums[i] != nums[i - 1])) {  // 跳过可能重复的答案
            int l = i + 1, r = nums.length - 1, sum = 0 - nums[i];
            while (l < r) {
                if (nums[l] + nums[r] == sum) {
                    ls.add(Arrays.asList(nums[i], nums[l], nums[r]));
                    while (l < r && nums[l] == nums[l + 1]) l++;
                    while (l < r && nums[r] == nums[r - 1]) r--;
                    l++;
                    r--;
                } else if (nums[l] + nums[r] < sum) {
                    while (l < r && nums[l] == nums[l + 1]) l++;   // 跳过重复值
                    l++;
                } else {
                    while (l < r && nums[r] == nums[r - 1]) r--;
                    r--;
                }
            }
        }
    }
    return ls;
}

12.给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。

输入: 
[
  [1,1,1],
  [1,0,1],
  [1,1,1]
]
输出: 
[
  [1,0,1],
  [0,0,0],
  [1,0,1]
]
进阶:
一个直接的解决方案是使用  O(mn) 的额外空间,但这并不是一个好的解决方案。
一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。
你能想出一个常数空间的解决方案吗?

思路解析:利用一个辅助数组来标记为0的位置,再根据它来做替换。

public void setZeroes(int[][] matrix) {
    int h=matrix.length;
    int w=matrix[0].length;
    int[][] nub=new int[h][w];
    for(int i=0;i<h;i++){
        for(int j=0;j<w;j++){
            if(matrix[i][j]==0){
                nub[i][j]=1;
            }
        } 
    }
    for(int i=0;i<h;i++){
        for(int j=0;j<w;j++){
            if(nub[i][j]==1){
                change(i,j,w,h,matrix);
            }
        } 
    }
    
}

public void change(int i,int j,int w,int h,int[][] matrix) {
    for(int k=0;k<w;k++){
       matrix[i][k]=0;
   }
   for(int l=0;l<h;l++){
       matrix[l][j]=0;
   }
}

13.给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。

示例:

输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]
说明:

所有输入均为小写字母。
不考虑答案输出的顺序。

 思路解析:每次循环取一个字符串,将它变为字符数组排序,利用map来存入,当然存入前利用containKey()比较是否存在

public List<List<String>> groupAnagrams(String[] strs) {
    if (strs.length == 0) return new ArrayList();
    Map<String, List> ans = new HashMap<String, List>();
    for (String s : strs) {
        char[] ca = s.toCharArray();
        Arrays.sort(ca);
        String key = String.valueOf(ca);
        if (!ans.containsKey(key)) ans.put(key, new ArrayList());
        ans.get(key).add(s);
    }
    return new ArrayList(ans.values());
}

 

14.假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。你可以假设数组中不存在重复的元素。

你的算法时间复杂度必须是 O(log n) 级别。

示例 1:

输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4

思路解析:将数组一分为二,其中一定有一个是有序的,另一个可能是有序,也能是部分有序。此时有序部分用二分法查找。无序部分再一分为二,其中一个一定有序,另一个可能有序,可能无序。就这样循环.

public int search(int[] nums, int target) {
    return search(nums,target,0,nums.length-1);
}
private int search(int[] nums,int target,int i,int j)
{
    if(i>j) return -1;
    int mid=i+(j-i)/2;
    if(nums[mid]==target) return mid;
    
    if(nums[i]<nums[j]){
        if(nums[mid]<target) return search(nums,target,mid+1,j);
        else return search(nums,target,i,mid-1);
    }
    else{
        int ans=search(nums,target,i,mid-1);
        return ans!=-1?ans:search(nums,target,mid+1,j);
    }
}

15.给定一个大小为 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在众数。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章