合并两个有序数组
合并两个有序数组LeetCode88
题目:给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3 输出:[1,2,2,3,5,6] 解释:需要合并 [1,2,3] 和 [2,5,6] 。 合并结果是 [1,2,2,3,5,6] 。
输入:nums1 = [1], m = 1, nums2 = [], n = 0 输出:[1] 解释:需要合并 [1] 和 [] 。 合并结果是 [1] 。
题目看起来很简单,可以用暴力遍历法比较两个数组的大小,定义p和q分别指向两个数组,比较它们当前指向数字大小,由于有向nums1插入数据的需要,这样会导致遍历时出现问题,故使用result存储遍历完之后的数据
var merge = function (nums1, m, nums2, n) {const result = [];let p = 0, q = 0, cur = 0;while (p < m || q < n) {if (p === m) {result[cur++] = nums2[q++];} else if (q === n) {result[cur++] = nums1[p++];}else if (nums1[p] <= nums2[q]) {result[cur++] = nums1[p];p++;} else if (nums1[p] > nums2[q]) {result[cur++] = nums2[q]q++;}}result.forEach((item, index) => {nums1[index] = item;})};
上面空间复杂度为O(n),由于nums1的实际长度其实是m+n,所以可以降低空间复杂度,从后往前遍历,那么数据也是从后往前插入,不会影响nums1的遍历,这样就不需要用一个新数组来存储结果了
var merge = function (nums1, m, nums2, n) {let p = m-1, q = n-1, cur = 0, tail = m + n -1;while (p >= 0 || q >= 0) {if (p === -1) {cur = nums2[q--];} else if (q === -1) {cur = nums1[p--];} else if (nums1[p] <= nums2[q]) {cur = nums2[q--];} else if (nums1[p] > nums2[q]) {cur = nums1[p--];}nums1[tail--] = cur;}};
轮转数组
轮转数组LeetCode189
题目:给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
输入: nums = [1,2,3,4,5,6,7], k = 3 输出: [5,6,7,1,2,3,4]
输入:nums = [-1,-100,3,99], k = 2 输出:[3,99,-1,-100]
向右轮转k个位置相当于把数组后k个元素平移到数组前面来
方法一:将数组后k个元素剪切并push到数组前面
var rotate = function (nums, k) {const length = nums.length;const count = k % length;nums.unshift(...nums.splice(length - count, count));};
方法二:将整个数组反转,在对从k开始的左右两个部分数组再次反转,就达到了平移的目的
var rotate = function (nums, k) {const reverse = function (arr, left, right) {let start = left;let end = right;while (start < end) {const temp = arr[start];arr[start++] = arr[end];arr[end--] = temp;}}const length = nums.length;const count = k % length;reverse(nums, 0, length - 1);reverse(nums, 0, count - 1);reverse(nums, count, length - 1);};
方法三:以k位的速度不停的交换元素,直到所有的元素都交换一遍(环状替换),从第一个元素开始交换,按照每隔k位开始交换,即第0位交换到第k位,保存第k位原来的值,与第(k+k)%length交换…
交换元素存在两个问题
- 怎么确定这样交换能交换到每一个元素
- 交换多少次停止,不设置停止条件会一直交换下去
对于第一个问题,存在两种情况
- 每次交换的都是不重复的位置,那么交换length次即可满足要求
- 交换的元素以一定的频率重复,比如长度为6,k为2时,每次都是第0位->第2位->第4位->第0位…重复,这么循环下去,每次交换都是重复的几个元素,所以需要在重复交互之前把元素挪到移动到下一个位置,以下一个位置为起点在进行交换,重复k%length次,就可以完整交互完整个数组了
分析完第一个问题就知道第二个问题的答案了,不管是第一种情况还是第二种情况都只需要交换length次就可满足要求
var rotate = function (nums, k) {const length = nums.length;if (k % length === 0) return;let count = 0;let origin = 0;let current = 0;let temp = nums[current];// 分为两种场景,每次交换的都是不重复的位置、交换的元素以一定的频率重复// 如果是第二种情况,交换的时候总会回到原点,故需保存原点用作对比while (count < length) {// 对k取余,元素i的位置需要移到到v位current = (current + k) % length;const item = nums[current];nums[current] = temp;temp = item;if (current === origin) {origin++;current = origin;temp = nums[current];}count++;}};
