算法-(一维数组)
算法-(一维数组)
在我觉得算法是一种能够提升程序性能和面试必备的利器,尤其是去外企,或者一些好的企业。并且在写算法的同时,会锻炼到我们的逻辑思考能力和空间想象能力。所以接下来我想聊聊一些常用的算法,在之前的文章中聊到了一些零散的算法,比如翻转链表,以及深度拷贝链表。但是那些都不成体系。所以接下来的文章,我想聊聊把相同的算法知识点放在一起去聊聊。那本篇就从最基础的关于数组的算法开始聊起,并且我会提炼出一些解题方式,当然同时也有demo。
合并数组
1.合并两个数组
- 【前提】: 给两个递增的整数数组【nums1】和【nums】,和两个整数【m】和【n】分别表示两个数组的元素数量多少。
- 【要求】:合并两个数组,并且合并后的数组按照非递减的顺序进行排列。
- 【输入eg】:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
- 【输出eg】:[1,2,2,3,5,6]
- 【想法】:
- 直接合并数组二到数组一,然后对数组一进行排序(最简单,但是没有技术含量,并且时间复杂度是O(n))
View Code- 两个数组进行比较,因为数组不能直接替换,需要移动插入。已经知道两个数组都是依次递增,所以我们使用【从后向前】的插入方式是【最少移动数组元素】的最好选择。
- 对数组一和数组二进行比较,从后向前比较
- 第一轮:如果数组1的最后一个小于数组二的最后一个,则把数组二的最后一个元素放在数组一的最后元素中 那就变成了 【123006】。然后第二个数组前移,第一个数组下标不变。同时赋值的下标增加一。
- 以此类推,如果数组1中的一个元素小于数组二中的某个元素,那就把数组一中的这个元素放在上次赋值位的下一位。
View Code2.合并n个数组
- 【想法】:可以采用归并的思想,把数组进行两两合并。比如说现在要合并4个数组,那我就写一个方法,这个方法的返回值是一个数组,传递的参数是两个数组。在方法内,把这两个数组放在一个新建的数组中,并返回新建的数组。这个就好办了,循环对比两个数组,数组一的元素大就把数组二的元素放在新数组中,最后把剩余的元素直接放在数组中。
View Codeprivate static int[] mergeTwoArray(int[] arr1, int[] arr2) { int length1 = arr1.length; int length2 = arr2.length; int[] newArr = new int[length1 + length2]; int i=0; // 数组一的指针 int j=0; // 数组二的指针 int k=0; while (j< length1 && k<length2){ if (arr1[j] <= arr2[k]){ newArr[i++]=arr1[j++]; }else { newArr[i++]=arr2[k++]; } } //如果那个数组没有循环完成,就直接拼接到后面。 while (j<length1){ newArr[i++]=arr1[j++]; } while (k<length2){ newArr[i++]=arr2[k++]; } return newArr; }
删除数组元素
1. 删除数组中和所给数字一样的元素。
- 【前提】: 给一个数组nums和一个值val
- 【要求】:移除数组中所有数值是val的,并且返回移除后的数组长度,不能使用额外的数组,必须使用o(1)额外空间,并且原地修改输入的数组。元素的顺序可以改变。
- 【输入】:nums=【3,2,2,3】 val =【3】
- 【输出】:2 ,nums【2,2】
- 【想法】:
- 【双指针交换移除】左右两个指针向中间走,实际上就是和原来数字不同的都放在左边,相同的都放在右边。
- 左边的指针如果和所给的数字一样,则把右侧的指针所指的地方的数据放在左侧,并且左侧不动,右侧继续前进。
- 如果不一样,左侧的指针则向前继续走。
View Code// 从两端开始,向中间遍历。 private static int removeElement2(int[] nums, int val) { int ans = nums.length; // 当两个指针相遇,则循环结束 for (int i = 0; i < ans; ) { //一个指针从左边开始如果和给定的数字相同,则把右边的数据给最左边,同时右边的指针减一 if (nums[i]==val){ nums[i]=nums[ans-1]; ans--; }else { i++; } } return ans; }- 【双指针移动】循环每个元素,当前循环元素如果和所给元素不一样,则把当前元素复制给前一个元素,并且变量增加一,这样的话,前面的元素都不是所给的元素,一直到循环结束,返回变量。
View Codeprivate static int removeElement(int[] nums, int val) { int ans = 0; for (int num : nums) { if (num != val) { nums[ans] = num; ans++; } } return ans; }2. 删除有序数组的重复项
- 【前提】: 给一个有序数组nums
- 【要求】:删除数组中相同的数字,让每个元素只能出现一次
- 【输入】:nums =[1,1,2]
- 【输出】:2 ,nums【1,2】
- 【想法】:快慢指针,快指针先走,慢指针在后,当快指针发现和和慢指针一样的时候,慢指针停止移动,快指针继续向前走,当快指针发现和慢指针不一样的时候,快指针把他当前的位置放在慢指针的下一个位置,然后慢指针向前移动到新元素的位置上。
- 比如: 1,1 ,2 快指针的下标在1,慢指针的下标在0
- 这个时候快指针的位置是1,慢指针也是1,那慢指针就不动了,
- 快指针移动到2【下标是2的位置上的数字也是2】
- 这个时候快指针发现2 和 慢指针位置上你的一不一样,那么就把这个2移动到慢指针的下一位,就变成了1,2,1.
- 当然我们维护了一个变量去记录变化的次数,所以只用打印变量的次数的内容就行,那就是1 ,2
View Code// 这里其实就是快慢指针,都是从头开始遍历,当慢指针等于快指针的位置的时候, // 慢指针不变,快指针继续向前,当他们不相同的时候,把快指针的内容赋值给慢指针的后一位数据,慢指针的指针数也随着增加一 private static int removeElement(int[] nums) { int length = nums.length; // slow 初始为1的原因是,题目要求每个元素只能出现一次 int slow = 1; for (int fast = 0; fast < length; fast++) { if (nums[slow - 1] != nums[fast]) { nums[slow] = nums[fast]; slow++; } } return slow; }
元素奇偶移动
1.奇偶数分离
- 【前提】: 给一个整数数组
- 【要求】:让数组中偶数位于数组前面,奇数位于数组后面
- 【输入】:【3,1,2,4】
- 【输出】:【2,4,3,1】or 【4,2,3,1】 or 【2,4,1,3】 or 【4,2,1,3】 只要满足奇数在前都可,不论任何顺序
- 【想法】:
- 【新数组赋值】:循环把奇数放在数组的前面,再循环放偶数。
View Codeprivate static int[] sortArrayByParityLevel1(int[] A) { int[] ans = new int[A.length]; int t = 0; for (int i = 0; i < ans.length; i++) { // 是偶数就放在数组前面 if (A[i] % 2 == 0) { ans[t++] = A[i]; } } for (int i = 0; i < ans.length; i++) { if (A[i] % 2 != 0) { ans[t++] = A[i]; } } return ans; }- 【双指针交换寻找】:
- 还是和上面数组删除元素的方法一样。一个指针从前面走,一个指针从后面走。
- 然后两个指针进行对比,如果前面的是偶数,后面的是奇数,则把位置进行交换
- 接着两个指针继续向前,直到交汇为止。
View Codeprivate static int[] sortArrayByParityd(int[] A) { int left = 0; int right = A.length - 1; for (int i = 0; i < A.length; i++) { // 左边大于右边,证明左边是奇数 那就把右边和的左边的进行颠倒 if (A[left] % 2 > A[right] % 2) { int temp = A[left]; A[left] = A[right]; A[right] = temp; } // 左边是偶数 左边向前推进 if (A[left] % 2 == 0) { left++; } // 左边不是偶数 右边边向前推进 if (A[left] % 2 != 0) { right--; } } return A; }2.调整后的顺序和原始数组顺序一致(和第一题的要求不同的是,在对他的奇偶换位置的同时,他们的顺序是不能变的)
- 【想法】:循环遍历,如果某个数的右边是偶数,左边是奇数,则把他们交换位置。
- 例【2,4,3,1】 第一轮 【2,3,1,4】第二轮 【3,1,2,4】
View Code//左边是偶数 右边是奇数 则进行交换 否则继续扫描 private static int[] reOrderArray(int[] array) { int n = array.length; for (int i = 0; i < n; i++) { for (int j = 0; j < n - 1 - i; j++) { // 左边是偶数,并且右边是奇数,则交换他们的位置 if ((array[j] & 1) == 0 && (array[j + 1] & 1) == 1) { int tmp = array[j]; array[j] = array[j + 1]; array[j + 1] = tmp; } } } return array; }