归并排序(单向链表/数组实现)

版权声明:仅限学习使用 https://blog.csdn.net/u014590889/article/details/89319730
/*
归并排序
(算法交换链表节点,时间复杂度O(nlogn),不考虑递归栈空间的话空间复杂度是O(1))
首先用快慢指针的方法找到链表中间节点,然后递归的对两个子链表排序,
把两个排好序的子链表合并成一条有序的链表。归并排序应该算是链表排序最佳的选择了,
保证了最好和最坏时间复杂度都是nlogn,
而且它在数组排序中广受诟病的空间复杂度在链表排序中也从O(n)降到了O(1)
*/
node_t* get_mid(node_t* head)
{
    node_t* fast = NULL;
    node_t* slow = NULL;
    
    if (NULL == head || head->next == NULL)
        return head;
    //使用 slow-fast方法找到链表的中间位置,注意这里写的链表的头节点是非空的,即头节点也是存贮数据的
    fast = head->next;  //   fast指向第2个节点
    slow = head;    //   slow指向第1个节点  
    while (fast != NULL&&fast->next != NULL)
    {
        fast = fast->next->next; //fast每次走两步
        slow = slow->next;  //slow每次走一步,这样slow就会到链表中点
    }
    //slow最后指向有n个元素的链表的第n/2个元素。
    //如一共6个元素,slow指向第3个,一共5个元素,slow指向第2个。
    return slow;
}

node_t* merge(node_t* newLeft, node_t* newRight)
{
    
    node_t * newList = NULL;  
    node_t * tail    = NULL;
    
    if (NULL == newLeft || NULL == newRight)
        return NULL;
    
    //注意指针tail才是后面进行操作的指针,newList是为了保存起点
    //printf ("i = %d\n", i++);//一直为0,说明递归时变量生存期只在函数内部
    if (newLeft->data < newRight->data)
    {
        newList = newLeft;
        newLeft = newLeft->next;
    }
    else
    {
        newList = newRight;
        newRight = newRight->next;
    }
    tail=newList;
    tail->next = NULL;
    //以上代码是向newList的第一个节点存入左右两个链表的头节点的较小的元素
    while (newLeft != NULL|| newRight != NULL)
    {
        if (newLeft == NULL)       //左边全部接完了
        {
            tail->next = newRight; //右边就直接整条链表接上去
            newRight = NULL;
        }
        else if (newRight == NULL) 
        {                          //同理,右边接完了
            tail->next = newLeft;  //左边就直接整条链表接上去,复杂度为O(1)
            newLeft = NULL;
        }
        else if (newLeft->data < newRight->data) 
        {
            tail->next = newLeft;  //上面接一整个链表,这里就是接链表中单个元素的操作
            newLeft = newLeft->next;    
            tail = tail->next;
            tail->next = NULL;
        }
        else 
        {
            tail->next = newRight;
            newRight = newRight->next;
            tail = tail->next;
            tail->next = NULL;
        }
    }
    return newList;    //返回新接好的List
}

node_t* listMergeSort(node_t* head) //传入需要归并排序的链表的头指针
{
    node_t* left     = NULL;
    node_t* right    = NULL;
    node_t* mid      = NULL;
    node_t* newLeft  = NULL;
    node_t* newRight = NULL;
    
    //一个元素就返回
    if (NULL == head || head->next == NULL)
        return head;
    mid = get_mid(head);//mid最后指向有n个元素的链表的第n/2个元素。
    left = head;
    right = mid->next;
    mid->next = NULL;
    //但是实际上将链表截断,左边的部分从第1个开始,右边的要从第4个(共6个)或者第3个(共5个)开始,所以有right=slow->next;
    //假设上面截断的左右两个子链表调用归并排序后变成了排好序(new,新)的“新”左右两个链表,然后开始归并
    printf("left head = %d_%d, right head = %d_%d\n", left->index, left->data, right->index, right->data);
    newLeft = listMergeSort(left);
    newRight = listMergeSort(right);
    printf("newLeft head = %d_%d, newRight head = %d_%d\n", newLeft->index, newLeft->data, newRight->index, newRight->data);

    return merge(newLeft, newRight);
}

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
 
void merge(int a[],int l,int r,int mid)
{
  int aux[r-l+1],i,j,k;
  
  for(k=l;k<=r;k++)
  aux[k-l]=a[k];
  
  i=l;
  j=mid+1;
  for(k=l;k<=r;k++)
  {
  	if(i>mid)
  	{
  		a[k]=aux[j-l];
  		j++;
	  }
	else if(j>r)
	{
		a[k]=aux[i-l];
		i++;
	  }
	else if(aux[i-l]>aux[j-l])
	{
		a[k]=aux[j-l];
		j++;
		}
	else
	{
		a[k]=aux[i-l];
		i++;
			}
				    
  	
	  }	
	
}
 
void merge_sort(int a[],int l,int r)
{
    if(l>=r)
	return ;
	
	int mid=(l+r)/2;
	
	merge_sort(a,l,mid);
	merge_sort(a,mid+1,r);
	merge(a,l,r,mid);	
	
}
 
 
void mergesort(int a[],int l,int r)
{
	merge_sort(a,l,r-1);
}

https://www.cnblogs.com/TenosDoIt/p/3666585.html

猜你喜欢

转载自blog.csdn.net/u014590889/article/details/89319730