chapter_sorting/merge_sort/ #61
Replies: 49 comments 55 replies
-
你好,想请问一下上述图片是通过什么制作的 |
Beta Was this translation helpful? Give feedback.
-
感谢大佬,对于菜鸟如我来说真是相见恨晚又恨早。期待后续更新! |
Beta Was this translation helpful? Give feedback.
-
public static int[] mergeSorting(int[] nums){
if (nums.length <= 1) {
return nums;
}
int num = nums.length >> 1;
int[] left = Arrays.copyOfRange(nums, 0, num);
int[] right = Arrays.copyOfRange(nums, num, nums.length);
return merge(mergeSorting(left), mergeSorting(right));
}
private static int[] merge(int[] left, int[] right) {
int i = 0, j = 0, k = 0;
int[] result = new int[left.length + right.length];
while(i < left.length && j < right.length){
if (left[i] <= right[j]) {
result[k++] = left[i++];
} else {
result[k++] = right[j++];
}
}
while (i < left.length) {
result[k++] = left[i++];
}
while (j < right.length) {
result[k++] = right[j++];
}
return result;
} 这样会不会更简洁 |
Beta Was this translation helpful? Give feedback.
-
今年实习机考,就是一个归并排序 |
Beta Was this translation helpful? Give feedback.
-
你好,将merge函数里的for循环,里的第三个else合并到第一个 if 里面可以吗,他俩的逻辑和第二个if一样的呀。我尝试之后,panic: runtime error: index out of range [2] with length 2 。 // 通过覆盖原数组 nums 来合并左子数组和右子数组
for k := left; k <= right; k++ {
// 若“左子数组已全部合并完”或“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
if i > leftEnd || tmp[i] > tmp[j] {
nums[k] = tmp[j]
j++
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
} else if j > rightEnd || tmp[i] <= tmp[j] {
nums[k] = tmp[i]
i++
// 否则,若“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
}
//else {
// nums[k] = tmp[j]
// j++
//}
} |
Beta Was this translation helpful? Give feedback.
-
问一下后续有动态规划的计划吗? |
Beta Was this translation helpful? Give feedback.
-
最近几天看了很2~3遍,收益匪浅!感想遇见!还望K神开通捐赠通道。 |
Beta Was this translation helpful? Give feedback.
-
清晰的,通过看图一下子明白原理是怎么样的。先看一遍K神的算法图,快速回忆一下算法,然后上课听老师讲效率高 |
Beta Was this translation helpful? Give feedback.
-
链表排序:具体实现细节比较复杂,有兴趣的同学可以查阅相关资料进行学习 |
Beta Was this translation helpful? Give feedback.
-
1 if (i > leftEnd) k神第1、2行是否冗余了,删除即可? |
Beta Was this translation helpful? Give feedback.
-
K神好,我刷Leetcode 剑指 Offer 51. 数组中的逆序对,用了上述代码,会有部分用例问题,但是不知道错在哪,能否请教下 class Solution {
int res = 0;
public int reversePairs(int[] nums) {
int left = 0;
int right = nums.length - 1;
mergeSort(nums, left, right);
return res;
}
public void mergeSort(int[] nums, int left, int right){
if(left >= right){
return;
}
int mid = (left + right) / 2;
mergeSort(nums, left, mid);
mergeSort(nums, mid + 1, right);
merge(nums, left, mid, right);
}
public void merge(int[] nums, int left, int mid, int right){
int[] tmp = Arrays.copyOfRange(nums, left, right + 1);
int leftStart = left - left, leftEnd = mid - left;
int rightStart = mid + 1 - left, rightEnd = right - left;
int i = leftStart, j = rightStart;
for(int k = left; k <= right; k++){
if(i > leftEnd){
nums[k] = tmp[j++];
}else if(j > rightEnd || tmp[i] <= tmp[j]){
nums[k] = tmp[i++];
}else{
System.out.println(mid - i + 1);
res += mid - i + 1;
nums[k] = tmp[j++];
}
}
}
} |
Beta Was this translation helpful? Give feedback.
-
这也太难了吧,那个merge()中的比较 tmp[i] 和 tmp[j] 的大小和i,j的各种操作,我看了好久,画图推了半天才看明白原理,但也就最多做到勉强看懂这么写能得到结果,这代码我只能感叹把指针玩出花来了,泰裤辣! |
Beta Was this translation helpful? Give feedback.
-
k神可以出一篇迭代和递归的算法。其他算法里总是提到这两个算法,看的多了,发现越看越迷糊 |
Beta Was this translation helpful? Give feedback.
-
func merge(nums: inout [Int], left: Int, mid: Int, right: Int) { |
Beta Was this translation helpful? Give feedback.
-
这个是链表形式的归并代码 class ListNode:
def __init__(self, val: int = 0):
self.val = val
self.next = None
class Solution:
def mergeSort(self, head: ListNode) -> ListNode:
if head or head.next:
return head
mid = self.getMiddle(head)
left = head
right = mid.next
mid.next = None
self.mergeSort(left)
self.mergeSort(right)
return self.merge(left, right)
def getMiddle(self, head: ListNode) -> ListNode:
slow = head
fast = head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
def merge(self, left: ListNode, right: ListNode) -> ListNode:
damn = ListNode()
temp = damn
while left and right:
if left.val <= right.val:
temp.next = left
left = left.next
else:
temp.next = right
right = right.next
temp.next = left if left else right
return damn.next |
Beta Was this translation helpful? Give feedback.
-
这是链表的归并Java实现代码,使用了递归,过会发一个不适用递归的,题目可见LeetCode 148
|
Beta Was this translation helpful? Give feedback.
-
这是不使用递归的归并排序
|
Beta Was this translation helpful? Give feedback.
-
归并排序的递归和非递归实现C语言代码 // 归并排序
void merge(float *x, int sl, int sr){
float *l_start = x, *r_start = x + sl, *tmp = (float *)malloc((sl + sr) * sizeof(float)); int i = 0, j = 0, k = 0;
while (i < sl && j < sr){ if (l_start[i] <= r_start[j]) { tmp[k++] = l_start[i++]; } else { tmp[k++] = r_start[j++]; } }
while (i < sl) { tmp[k++] = l_start[i++]; } while (j < sr) { tmp[k++] = r_start[j++]; }
for (i = 0; i < sl + sr; i++) { x[i] = tmp[i]; } free(tmp);
}
void merge_sort_rec(float *x, int size){ if (size > 1){ int sl = size / 2, sr = size - sl; merge_sort_rec(x, sl); merge_sort_rec(x + sl, sr); merge(x, sl, sr); } }
void merge_sort_nrec(float *x, int size) {
for (int width = 1; width < size; width *= 2) { // width of each part : 1, 2, 4, 8, ...; e.g. 1 + 1 -> 2; 2 + 2 -> 4; 4 + 4 -> 8; ...
int left = 0;
while (left < size) { // 合并每一对子数组
int mid = left + width - 1, right = (mid + width < size) ? mid + width : size - 1; // [left, mid], [mid+1, right] ; 此处限制了right的范围,避免超出数组的大小
if (mid < size - 1) { merge(x + left, mid - left + 1, right - mid); }// 当前子数组确实有需要合并的右侧部分,才进行合并操作。
left += 2 * width;
}
}
} |
Beta Was this translation helpful? Give feedback.
-
新入门菜鸡,我想问问上面的递归中归并排序空间复杂度不是O(nlogn),为什么写的时O(n),求 |
Beta Was this translation helpful? Give feedback.
-
K神,我不太习惯针对原数组进行操作,我习惯进行一个返回指,写成下面这样。如果写成下面这样,请问空间复杂度是不是会增加? |
Beta Was this translation helpful? Give feedback.
-
def merge(nums, left, mid, right):
i, j = left, mid + 1
tmp_array = []
while i <= mid and j <= right:
if nums[i] <= nums[j]:
tmp_array.append(nums[i])
i += 1
else:
tmp_array.append(nums[j])
j += 1
if i <= mid:
tmp_array.extend(nums[i:mid + 1])
else:
tmp_array.extend(nums[j:right + 1])
nums[left:right + 1] = tmp_array
def sort(nums, left, right):
if left >= right:
return
mid = (right + left) // 2
sort(nums, left, mid)
sort(nums, mid + 1, right)
merge(nums, left, mid, right)
nums = [1,3,32,4,1,-100,23,17,23,6,-7,2,9,-1000]
left = 0
right = len(nums) - 1
sort(nums, left, right)
print(nums) |
Beta Was this translation helpful? Give feedback.
-
js的一些文字游戏。效率肯定不比原文。但对我来说,更容易写。 /* 合并左右子数组 */
function merge(l, m, r) {
// 记录初始l和m
const init = { l, m }
// 临时数组
const arr = []
// 向右移动指针l和m,当左右子数组均非空时,比较nums[l]和nums[m+1]大小,取较小者加入临时数组尾部
while (l <= init.m && m + 1 <= r) arr.push(nums[
nums[l] <= nums[m + 1] ? l++ : m++ + 1
])
// 原数组[l,r]区间,使用[...临时数组,...左子数组残部,...右子数组残部]替换之
nums.splice(//in-place replacing
init.l,//start
r - init.l + 1,//delete count
...arr,//new items
...nums.slice(l, init.m + 1),//new items
...nums.slice(m + 1, r + 1),//new items
)
} |
Beta Was this translation helpful? Give feedback.
-
归并排序单链表: 首先求得链表的长度 length,然后将链表拆分成子链表进行合并。 具体做法如下。 用 subLength 表示每次需要排序的子链表的长度,初始时 subLength=1。 每次将链表拆分成若干个长度为 subLength 的子链表(最后一个子链表的长度可以小于 subLength),按照每两个子链表一组进行合并,合并后即可得到若干个长度为 subLength×2 的有序子链表(最后一个子链表的长度可以小于 subLength×2)。合并两个子链表仍然使用「21. 合并两个有序链表」的做法。 将 subLength 的值加倍,重复第 2 步,对更长的有序子链表进行合并操作,直到有序子链表的长度大于或等于 length,整个链表排序完毕。 如何保证每次合并之后得到的子链表都是有序的呢?可以通过数学归纳法证明。 初始时 subLength=1,每个长度为 1 的子链表都是有序的。 如果每个长度为 subLength 的子链表已经有序,合并两个长度为 subLength 的有序子链表,得到长度为 subLength×2 的子链表,一定也是有序的。 当最后一个子链表的长度小于 subLength 时,该子链表也是有序的,合并两个有序子链表之后得到的子链表一定也是有序的。 因此可以保证最后得到的链表是有序的。 using namespace std; struct ListNode class Solution
private:
}; void deleteList(ListNode* head) { int main()
} |
Beta Was this translation helpful? Give feedback.
-
谢谢分享! function mergeSort(nums) {
const len = nums.length
// 递归结束边界
// 交换顺序
if(len===2&&len[0]>len[1]){
return [len[1],len[0]]
}
if(len<2)return nums
// 取出中间index
const mid = parseInt(len/2)
// slice 会提取到但不包括 end 的位置。left是0~mid~1,right是mid~结束
const left = mergeSort(nums.slice(0,mid))
const right = mergeSort(nums.slice(mid))
let newArr = []
for (let i = 0; i < len; i++) {
// 左右两边不一定有值,将有值的加到新数组尾部
if(!left.length||!right.length){
newArr = newArr.concat(left.length===0?right:left)
break
}
// 左右都有值,判断左右两边最小的值,并加到新数组中
newArr.push(left[0]>=right[0]?right.splice(0,1)[0]:left.splice(0,1)[0])
}
return newArr
} |
Beta Was this translation helpful? Give feedback.
-
js中一个理解起来更简单的写法:
|
Beta Was this translation helpful? Give feedback.
-
Python 利用列表的追加合并简化代码: def merge(arr: list[int], l: int, m: int, r: int):
temp = []
i, j = l, m + 1
# merge two sorted sub-arrays into one sorted array
while i <= m and j <= r:
if arr[i] <= arr[j]:
temp.append(arr[i])
i += 1
else:
temp.append(arr[j])
j += 1
# append remaining elements of the sub-arrays
if i <= m:
temp += arr[i: m + 1]
if j <= r:
temp += arr[j: r + 1]
# copy the sorted array back to the original array
for i, val in enumerate(temp):
arr[l + i] = val |
Beta Was this translation helpful? Give feedback.
-
时间复杂度每层合并时的每次操作时间视为单位时间,则考虑操作次数,容易从合并代码的三次循环发现需要遍历完数组,或者说遍历数组的次数与数组长度n成正比,故每层时间复杂度为O(n),而递归操作次数与log2n成正比,所以最后为O(nlogn);而空间复杂度从栈帧空间来看的话,最深时为logn,每回溯一层到上一层就释放一次栈,然后记录一次合并的tmp,然后继续右子数组递归往下,然后递归到底又回溯重复许多这样的操作,直至回到第一层此时已完成左子数组的排序(记录下了一半的nums),栈帧空间只有一层的内容(都释放完了),然后重复左子数组的行为开始递归右子数组,最后得到另一半的nums,执行第一层的归并排序代码merge(nums, left, mid, right)。从整个流程来看,栈帧空间最大为logn,临时数组tmp最大为n(执行第一层的归并排序代码merge(nums, left, mid, right)时产生的临时数组tmp),取最大故O(n)。 |
Beta Was this translation helpful? Give feedback.
-
C 可用3元运算符简化处理左右子数组处的代码 while (i <= m && j <= r)
temp[size++] = arr[i] < arr[j]? arr[i++] : arr[j++]; |
Beta Was this translation helpful? Give feedback.
-
def merge_sort(nums: list[int]):
nums = [1, 3, 32, 4, 1, -100, 23, 17, 23, 6, -7, 2, 9, -1000] |
Beta Was this translation helpful? Give feedback.
-
chapter_sorting/merge_sort/
Your first book to learn Data Structure And Algorithm.
https://www.hello-algo.com/chapter_sorting/merge_sort/
Beta Was this translation helpful? Give feedback.
All reactions