算法题-数组算法题
题一:二分查找法-Java:用于有序数组
//实验数据 int[] arr = {3,21,34,46,55,71,74,80,97};
代码:
public int findIndex(int value, int[] arr) { // arr是有序数组 // value是目标值 int min = 0; //最大值,最小值的索引值 int max = arr.length - 1; int mid = (min + max) / 2; while (arr[mid] != value) { if (arr[mid] > value) { max = mid - 1; } else if (arr[mid] < value) { min = mid + 1; } mid = (min + max) / 2; if (min > max) return -1;//如果最小索引大于最大索引证明没有找到这个value,要退出循环 } return mid;//返回索引 }
题二:给定一个数组,写一个函数,将数组中的0都挪到数组的末尾,而维持其他非0元素的相对位置
//实验数据 int[] nums = {0,1,0,3,12}; //排序后 nums = {1,3,12,0,0}
思路:将所有非0元素拿出来,放在按顺序放在一个数组中,然后将最后的位置全部塞进0
public ArrayList moveZero(int[] nums) { ArrayList arrayList = new ArrayList(); for (int i = 0; i < nums.length; i++) { if (nums[i] != 0) { arrayList.add(nums[i]); } } int diff = nums.length - arrayList.size(); if (diff == 0) { return arrayList; } else { for (int i = 0; i < diff; i++) { arrayList.add(0); } return arrayList; } }
思路二:遍历所有数组,将非0元素都放在前K位,剩余的都填充0
public int[] moveZero2(int[] nums) { int k = 0;//在数组nums中,[0...k) 均为非0元素 //思路:在遍历的[0...i]个元素的数组中,所有的非0元素都按照顺序在[0...k)中 for (int i = 0; i < nums.length; i++) { if (nums[i] != 0) { //如果是非0元素,将该元素放在k位置 nums[k] = nums[i]; k++; //k++ } } //将剩余位置,都放置为0 for (int i = k; i < nums.length; i++) { nums[i] = 0; } return nums; }
题目三:给n个元素的数组,元素取值只有0,1,2三种可能,请为这个数组进行排序
思路一:使用计数排序,适用于元素个数非常有限的情况。遍历数据0,1,2的分别的元素个数,然后按照数值大小,在对应的位置分别放置多少个元素即可
public int[] sortColors(int[] nums) { int[] counts = {0, 0, 0}; // 遍历数组,查出数值个数 for (int i = 0; i < nums.length; i++) { if (nums[i] == 0) { counts[0]++; } else if (nums[i] == 1) { counts[1]++; } else if (nums[i] == 2) { counts[2]++; } else { return null; } } int index = 0; for (int i = 0; i < counts.length; i++) { //按照顺序塞入值 for (int j = 0; j < counts[i]; j++) { nums[index] = i; index++; } } return nums; }
思路二:三路快排,只需遍历一次即可: 将数组分为三部分,第一部分全是0 ,第二部分全是1,第三部分全是2。对第二部进行遍历:如果值=1,则往后遍历一位;如果值=2,将第三部分的左侧边界往左移动一位,并将i和左侧边界值交换位置(放在第三部分的最前端),i的值不变;同理,如果值=0,将第一部分的右侧值往右移动一位,并将i和右侧边界交换位置,(放在第一部分的最右端),i的值不变
public int[] sortColors2(int[] nums) { // 分为三部分,第一部分全是0 ,第二部分全是1,第三部分全是2 //初始化索引值 int zero = -1;//nums[0,zero] 第一部分 int two = nums.length;//nums[two,length-1] 第三部分 for (int i = 0; i < two; ) { if (nums[i] == 1) { i++; } else if (nums[i] == 2) { two--; //第三部分的左侧边界,往左减一位 swap(nums, two, i); //将i和two交换位置 } else if (nums[i] == 0) { zero++; //第一部分的右侧边界,往右加一位 swap(nums, zero, i); } else { return null; } } return nums; } // 交换数组元素 public static void swap(int[] array, int x, int y) { int xx = array[x]; int yy = array[y]; array[x] = yy; array[y] = xx; }
题四:在n长度的数组中,找到第k大的数值
题五:给定一个有序整型数组和整型数target,在数组中找两个元素,使其相加和为target,并返回开两个元素的索引
int[] nums = {2,7,11,15}; int target = 9; //结果返回的索引就是:1,2(索引从1开始算)
思路:使用二分查找,nums[i] 在剩余数组(也是有序的)中,寻找target-nums[i] (每一次遍历,都使用一次二分查找)
思路二:对撞指针,(双指针)一个指针在数组首,一个指针在数组尾,两个指针不停的往中间移动 O(n)
public int[] twoSum(int[] arr, int target) { if (arr.length < 2) return null; int l = 0; int r = arr.length - 1; int res[] = {0, 0}; while (l < r) { if ((arr[l] + arr[r]) == target) { res[0] = l + 1; res[1] = r + 1; return res; //返回索引数组 } else if ((arr[l] + arr[r]) < target) { l++; //最左侧往右移一位 } else { r--; //最右侧往左移一位 } } throw null; //如果没有找到,抛出异常 }
题六:给定一个数组和数字s,找到数组中的最短连续子数组,使得连续子数组的数字和sum>=s,返回这个最短子数组的长度
//实验数据 int[] nums={2,3,1,2,4,3}; int s = 7; //输出结果:子数组[3,4],长度为2
思路:滑动窗口,子数组[l,r],当子数组的和<s时,将子数组的右侧往右移动,子数组增加一位;当子数组的和>=s时,将子数组的左侧往右移一位,子数组减少一位。
public int minSubArr(int[] nums, int s) { int l = 0, r = -1; //nums[l...r]是我们的滑动窗口 int sum = 0; int res = nums.length + 1; while (l < nums.length) { //左边界 < 长度 if (r + 1 < nums.length && sum < s) { //当前窗口的和 <s r++; //将右边界,往右移动,增加一位 sum += nums[r]; } else {
l++; //将左边界,往右移动,减少一位 sum -= nums[l]; } if (sum >= s) { res = min(res, r - l + 1); } } if (res == nums.length + 1) { //如果没有,则返回0 return 0; } return res; } //求最小值 public int min(int i, int j) { if (i < j) { return i; } else { return j; } }
题七:寻找字符串中,没有重复字母的最长子字符串
//示例数据: "abcabcbb" -> "abc" "nnnnn" -> "n" "pwwkew" -> "wke"
思路:滑动窗口 ,滑动窗口的长度并不是固定不变的。每次对比下一个字符是否出现在子字符串时,使用一个频率数组,记录每个字符在子字符串中出现的频率。如果为0,那就代表没有在子字符串中出现过;如果非0,就代表出现过