什么是归并排序?
归并排序的思想:将原数组不断拆分,一直拆到每个子数组只有一个元素时,第一阶段结束。然后开始“并”,将相邻两个数组合并为一个有序的数组,直到整个数组有序。
归并排序时间复杂度:归并排序是一个稳定的 O(nlogn) 排序算法,此处的稳定指的是时间复杂度稳定且归并排序也是一个稳定性排序算法。
时间复杂度稳定:归并排序拆分的过程类似于树的结构,递归的深度就是我们拆分数组所用的时间,就是树的深度,为O(logn)。而合并两个数组的过程 merge 操作就是一个数组的遍历过程,为O(n)。
归并排序图解:
合并过程图解:
归并排序代码
代码如下:
public static void mergeSort(int[] arr){
mergeSortInternal(arr,0,arr.length-1);
}
private static void mergeSortInternal(int[] arr, int l, int r) {
if (l>=r){
return;
}
int mid=l+(r-l)/2;
//将原数组拆分成左右两个小区间
mergeSortInternal(arr,l,mid);
mergeSortInternal(arr,mid+1,r);
merge(arr,l,r,mid);
}
private static void merge(int[] arr, int l, int r, int mid) {
//1.定义一个临时数组,将arr里的元素拷贝进去
int[] aux=new int[r-l+1];
for (int i = 0; i < aux.length; i++) {
aux[i]=arr[i+l];
}
int i=l; //i为左侧小数组的起始位置
int j=mid+1; //j为右侧小数组的起始位置
for (int k=l;k<=r;k++){ //k遍历的是arr数组
if (i>mid){ //左侧小数组已经处理完了,直接将右侧剩下的元素拷贝过去
arr[k]=aux[j-l];
j++;
}else if(j>r){ //右侧小数组已经处理完了,直接将左侧剩下的元素拷贝过去
arr[k]=aux[i-l];
i++;
}else if(aux[i-l]<=aux[j-l]){//当左侧区间的元素大于右侧区间的元素时
arr[k]=aux[i-l];
i++;
}else{
arr[k]=aux[j-l];
j++;
}
}
}
归并排序的优化:
- 当左右两个子区间走完子函数后,如果此时 arr[mid] < arr[mid+1],说明 arr[mid] 已经时左区间的最大值了,arr[mid+1] 已经是右区间的最小值了,整个区间已经有序,不需要再执行 merge 操作了!!!
- 在小区间上,我们可以直接使用插入排序来优化,可以减少递归的次数!!!
优化后的代码如下:
public static void mergeSort(int[] arr){
mergeSortInternal(arr,0,arr.length-1);
}
private static void mergeSortInternal(int[] arr, int l, int r) {
//优化1:小区间上使用插入排序
if (r-l<=15){
insertionSort(arr,l,r);
return;
}
int mid=l+(r-l)/2;
mergeSortInternal(arr,l,mid);
mergeSortInternal(arr,mid+1,r);
//优化2:只有两个区间还存在元素先后顺序不同时才合并
if (arr[mid]>arr[mid+1]) {
merge(arr, l, r, mid);
}
}
private static void merge(int[] arr, int l, int r, int mid) {
int[] aux=new int[r-l+1];
for (int i = 0; i < aux.length; i++) {
aux[i]=arr[i+l];
}
int i=l;
int j=mid+1;
for (int k=l;k<=r;k++){
if (i>mid){
arr[k]=aux[j-l];
j++;
}else if (j>r){
arr[k]=aux[i-l];
i++;
}else if (aux[i-l]<=aux[j-l]){
arr[k]=aux[i-l];
i++;
}else{
arr[k]=aux[j-l];
j++;
}
}
}
public static void insertionSort(int[] arr,int l,int r){
for (int i = l+1; i <=r ; i++) {
for (int j = i; j >0 ; j--) {
if (arr[j]>=arr[j-1]){
break;
}else{
swap(arr,j,j-1);
}
}
}
}
public static void swap(int[] arr,int i,int j){
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
归并排序相关习题
148.排序链表
题目描述如下:
代码如下:
public ListNode sortList(ListNode head){
if (head==null||head.next==null){
return head;
}
ListNode mid=getMidNode(head);
ListNode rightNode=mid.next;
mid.next=null;
//递归拆分链表
ListNode l=sortList(head);
ListNode r=sortList(rightNode);
return merge(l,r);
}
//合并
private ListNode merge(ListNode l, ListNode r) {
ListNode dummyHead=new ListNode(0);
ListNode cur=dummyHead;
while (l!=null&&r!=null){
if (l.val<r.val){
cur.next=l;
l=l.next;
cur=cur.next;
}else{
cur.next=r;
r=r.next;
cur=cur.next;
}
}
if (l==null){
cur.next=r;
}
if (r==null){
cur.next=l;
}
return dummyHead.next;
}
//获取链表中间节点(快慢指针)
public ListNode getMidNode(ListNode head){
ListNode fast=head.next.next;
ListNode low=head;
while (fast!=null&&fast.next!=null){
fast=fast.next.next;
low=low.next;
}
return low;
}
剑指 Offer 51.数组中的逆序对
题目描述如下:
解题思路:
代码如下:
public int reversePairs(int[] nums) {
return reversePairsHelper(nums,0,nums.length-1);
}
private int reversePairsHelper(int[] nums, int l, int r) {
if (l>=r){
return 0;
}
int mid=l+(r-l)/2;
//1.先求出左区间的逆序对个数
int left=reversePairsHelper(nums, l, mid);
//2.求出右区间的逆序对个数
int right=reversePairsHelper(nums, mid+1, r);
if (nums[mid]>nums[mid+1]){
//如果此时左右区间还存在逆序,合并
return merge(nums,l,mid,r)+left+right;
}
//此时左右区间已经有序,逆序对个数就是左右区间逆序对个数之和
return left+right;
}
//合并,并且返回合并过程中逆序对的个数
private int merge(int[] nums, int l, int mid, int r) {
//合并过程中产生的逆序对
int count=0;
int[] aux=new int[r-l+1];
for (int i = 0; i < aux.length; i++) {
aux[i]=nums[i+l];
}
int i=l;
int j=mid+1;
for (int k=l;k<=r;k++){
if (i>mid){
nums[k]=aux[j-l];
j++;
}else if (j>r){
nums[k]=aux[i-l];
i++;
}else if (aux[i-l]<=aux[j-l]){
nums[k]=aux[i-l];
i++;
}else{
//左区间元素大于右区间元素,构成逆序对
//记录逆序对的个数
count+=mid-i+1;
nums[k]=aux[j-l];
j++;
}
}
return count;
}
总结
以上就是今天要讲的内容,本文介绍了七大排序中的归并排序,包括归并排序的思路、代码和相关习题,如果你觉得有收获的话,那就留下你的
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)