一:对数组进行归并排序
目录
归并排序是利用归并的思想实现的排序方法,该方法采用经典的分治策略,分是将一个问题分成许多小的问题然后递归求解,治则将分的阶段得到的答案修补在一起,即分而治之。
public class MergeSort {
public static void main(String[] args) {
//test();
int[] array={8,4,5,7,1,3,6,2};
int[] newArr=new int[array.length];//归并排序需要一个额外空间
mergeSort(array,0,array.length-1,newArr);
System.out.println(Arrays.toString(array));
}
//疯狂的测试用例800000
public static void test(){
int[] array=new int[8000000];
int[] newArr=new int[array.length];
for (int i = 0; i < 8000000; i++) {
array[i]=(int) (Math.random()*80000000);
}
Date date1 = new Date();
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-d:HH:mm:ss");
String format1= simpleDateFormat.format(date1);
System.out.println("排序前的时间"+format1);
mergeSort(array,0,array.length-1,newArr);
Date date2 = new Date();
String format2 = simpleDateFormat.format(date2);
System.out.println("排序后的时间"+format2);
}
//递归方法无返回值,所以退出递归的条件是当数组的左指针大于右指针时
public static void merge(int[] array,int left,int right,int mid,int[] newArr){
int i=left;
int j=mid+1;
int temp=0;
while(i<=mid && j<=right) {
//如果左边的有序序列的当前元素小于右边有序序列的当前元素
if (array[i] < array[j]) {
//把左边的当前元素拷贝到新数组中
newArr[temp] = array[i];
//指针后移
i++;
temp++;
} else {
//把右边的当前元素拷贝到新数组中
newArr[temp] = array[j];
j++;
temp++;
}
}
//如果数组的左半部分中的元素全部取完,即数组右半部分还有剩余元素
while (j<=right){
//就把数组右半部分的剩余元素依次添加到新数组中
newArr[temp++]= array[j++];
}
//如果数组的右半部分中的元素全部取完
while (i<=mid){
//就把数组左半部分的剩余元素依次添加到新数组中
newArr[temp++]= array[i++];
}
//把新数组中的所有元素拷贝到array中
temp=0;
int tempLeft=left;
while(tempLeft<=right){
array[tempLeft++]=newArr[temp++];
}
}
public static void mergeSort(int[] array,int left,int right,int[] newArr){
if(left<right){
//int mid=(left+right)/2;
int mid=left+(right-left)/2;
//向左递归分解
mergeSort(array,left,mid,newArr);
//向右递归分解
mergeSort(array,mid+1,right,newArr);
//合并
merge(array,left,right,mid,newArr);
}
else{
return;
}
}
}
二:对链表进行归并排序
leetcode148:排序链表
思路分析:
1:与数组的归并排序基本相同,首先要将;链表进行拆分,那就需要找到链表的中间节点,递归的将链表分成左半部分和右半部分。因此我们需要找到链表的中间节点。
2:这里我们利用快慢双指针的方法寻找链表的中间节点
3:定义一个虚拟头结点,用于连接链表的节点,并定义一个指向虚拟头结点的指针和指向链表左半部分头结点和右半部分头结点的指针。将拆分后的链表中的每个元素进行排序比较的,将值较小的节点挂载到虚拟头结点之后,并将指针后移
4:当退出while循环时,说明链表左半部分或者右半部分有一个已经遍历完毕,即指针指向空值了,此时进行判断,如果左半部分遍历完毕,就将右半部分挂载到虚拟头结点之后,同理,右半部分遍历完毕,就将左半部分挂载到虚拟头结点之后。
class Solution {
public ListNode sortList(ListNode head) {
return mergeSort(head,null);
}
public ListNode mergeSort(ListNode left,ListNode right){
if(left==null){
return left;
}
if (left.next == right) {
left.next = null;
return left;
}
//中间节点
ListNode midNode=getMid(left,right);
ListNode leftNode= mergeSort(left,midNode);
ListNode rightNode= mergeSort(midNode,right);
//节点节点
ListNode resNode = merge(leftNode,rightNode);
return resNode;
}
public ListNode merge(ListNode leftNode,ListNode rightNode){
//定义一个虚拟头结点,用于连接有序链表
ListNode preNode=new ListNode(0);
//定义一个指向虚拟头结点的指针temp
ListNode temp=preNode;
//指向链表左半部分第一个节点的指针
ListNode leftTemp=leftNode;
//指向链表右半部分第一个节点的指针
ListNode rightTemp=rightNode;
//当左指针与右指针都不指向空时,循环比较
while(leftTemp!=null&&rightTemp!=null){
//比较递归后链表的左半部分第一个值值与右半部分第一个值的大小
if(leftTemp.val<=rightTemp.val){
temp.next=leftTemp;
//左指针后移
leftTemp=leftTemp.next;
}else{
temp.next=rightTemp;
//右指针后移
rightTemp=rightTemp.next;
}
//temp节点后移
temp=temp.next;
}
//当退出循环时,说明要么链表的左半部分遍历完了即左指针指向空了,但是右半部分没有
if(rightTemp!=null){
//把右半部分挂载到temp节点之后
temp.next=rightTemp;
}
//要么链表的右半部分遍历完了即右指针指向空了,但是左半部分没有
if(leftTemp!=null){
//把右半部分挂载到temp节点之后
temp.next=leftTemp;
}
return preNode.next;
}
//定义一个寻找中间节点的方法
public static ListNode getMid(ListNode left,ListNode right){
//利用快慢双指针寻找链表的中间节点
ListNode fast=left;
ListNode slow=left;
while(fast!=right && fast.next!=right){
fast=fast.next.next;
slow=slow.next;
}
return slow;
}
}