链表的快排、归并排序

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_25026989/article/details/89521473

引言
面试被问到了如何对链表进行排序, 快排不敢写,写了个很低效的插入排序。
现在整理一下。

解法
1、链表的归并排序
Node* listMergeSort(Node* head)// n list里面节点的个数
{

	//	if (head->next == NULL) //一个元素就返回
	if (head->next == NULL)
		return head;
 
	Node* fast = head->next;
	Node* slow = head;
	while (fast != NULL&&fast->next != NULL)
	{
		fast = fast->next->next;
		slow = slow->next;
	}
 
	Node* left = head;
	Node* right = slow->next;
	slow->next = NULL;
 
 
	Node* newLeft = listMergeSort(left);
	Node* newRight = listMergeSort(right);
 
	
 
	Node * newList ;
	Node * tail ;
 
 
	if (newLeft->data < newRight->data)
	{
		newList = newLeft;
		newLeft = newLeft->next;
	}
	else
	{
		newList = newRight;
		newRight = newRight->next;
	}
		tail=newList ;
		tail->next = NULL;
 
		while (newLeft != NULL|| newRight != NULL)
	{
		if (newLeft == NULL)
		{
			tail->next = newRight;
			newRight = NULL;
		}
		else if (newRight == NULL) {
			tail->next = newLeft;
			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;
		}
	}
	Node *temp = newList;
	return newList;
}

2、快速排序

void quicksort(Linklist head, Linklist end){  
    if(head == NULL || head == end)             //如果头指针为空或者链表为空,直接返回  
        return ;  
    int t;  
    Linklist p = head -> next;                  //用来遍历的指针  
    Linklist small = head;  
    while( p != end){  
        if( p -> data < head -> data){      //对于小于轴的元素放在左边  
            small = small -> next;  
            t = small -> data;  
            small -> data = p -> data;  
            p -> data = t;  
        }  
        p = p -> next;  
    }  
    t = head -> data;                           //遍历完后,对左轴元素与small指向的元素交换  
    head -> data = small -> data;  
    small -> data = t;  
    quicksort(head, small);                     //对左右进行递归  
    quicksort(small -> next, end);  
}  

快排复习

快速排序主要是partition的过程,partition最常用有以下两种写法:

第一种:

int mypartition(vector<int>&arr, int low, int high)
{
    int pivot = arr[low];//选第一个元素作为枢纽元
    while(low < high)
    {
        while(low < high && arr[high] >= pivot)high--;
        arr[low] = arr[high];//从后面开始找到第一个小于pivot的元素,放到low位置
        while(low < high && arr[low] <= pivot)low++;
        arr[high] = arr[low];//从前面开始找到第一个大于pivot的元素,放到high位置
    }
    arr[low] = pivot;//最后枢纽元放到low的位置
    return low;
}

第二种:

int mypartition(vector<int>&arr, int low, int high)
{
    int pivot = arr[high];//选最后一个元素作为枢纽元
    int location = low-1;//location指向比pivot小的元素段的尾部
    for(int i = low; i < high; i++)//比枢纽元小的元素依次放在前半部分
       if(arr[i] < pivot)
           swap(arr[i], arr[++location]);
    swap(arr[high], arr[location+1]);
    return location+1;
}

当第二种方法也可以选择第一个元素作为枢纽(当我们对链表进行快排时选用这种做法)

int mypartition(vector<int>&arr, int low, int high)
 {
     int pivot = arr[low];//选第一个元素作为枢纽元
     int location = low;//location指向比pivot小的元素段的尾部
     for(int i = low+1; i <= high; i++)//比枢纽元小的元素依次放在前半部分
     if(arr[i] < pivot)
         swap(arr[i], arr[++location]);
     swap(arr[low], arr[location]);//注意和前面的区别,是为了保证交换到头部的元素比pivot小
     return location;
 
 }

快排主函数如下:

void quicksort(vector<int>&arr, int low, int high)
{
    if(low < high)
    {
        int middle = mypartition(arr, low, high);
        quicksort(arr, low, middle-1);
        quicksort(arr, middle+1, high);
    }
}

快排非递归实现

主要思想是用栈来保存子数组的左右边界,一下代码中用数组模拟栈

void quicksort_unrecursion(vector<int>&arr)//快速排序非递归
 {
     int mystack[2000];//假设递归不超过1000层
     //栈中保存下次需要排序的子数组的开始位置和结束位置
     int top = -1;
     mystack[++top] = 0;
     mystack[++top] = arr.size() - 1;
     while(top > 0)//栈非空
     {
         int high = mystack[top--], low = mystack[top--];
         int middle = mypartition(arr, low, high);
         if(middle+1 < high)//右边子数组入栈
         {
             mystack[++top] = middle+1;
             mystack[++top] = high;
         }
         if(low < middle-1)//左边子数组入栈
         {
             mystack[++top] = low;
             mystack[++top] = middle-1;
         }
     }
 }

堆排复习

          int size;
          int a[] = new int[] { 23,22,12,88,34};
         //更新最大堆操作
          void  heaplify(int x) {                         //参数为父亲节点下标

            int lchild=x*2;                           //左孩子下标

            int rchild=x*2+1;                         //右孩子下标

            int max=x;                                //最大值下标初始为父亲下标

          if(lchild<size&&a[lchild]>a[max])           //比较找出最大值

               max=lchild;

          if(rchild<size&&a[rchild]>a[max])

              max=rchild;

          if(max!=x){           //若父亲节点为最大值,则符合性质,否则交换,将最大值移到父亲节点处,然后因为孩子节点处已改变,更新此节点。

            int t=a[max];

                a[max]=a[x];

                a[x]=t;

             heaplify(max);

           }

       }

   //建最大堆操作

          void createHeap(){
 
          for(int i=a.length/2+1;i>=0;i--){     
          //叶子结点数为结点总数一半且都在最后,可以从孩子节点下标的算法为父亲节点*2                 
             heaplify(i);                       // a.length/2+1处开始为非叶子节点     
          }

      }

   //堆排序操作

       void sort(){

        createHeap();                                //建最大堆

        for(int i=size-1;i>=1;i--){               //每次将第一个数与最后一个数交换,然后大小-1,更新已经改变的根节点

            int t=a[0];

                a[0]=a[size-1];

                a[size-1]=t;

                size--;

                heaplify(0);

         }

     }

非递归版本

import java.util.Arrays;

/**
 * 堆排序demo
 */
public class HeapSort {
    public static void main(String []args){
        int []arr = {9,8,7,6,5,4,3,2,1};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void sort(int []arr){
        //1.构建大顶堆
        for(int i=arr.length/2-1;i>=0;i--){
            //从第一个非叶子结点从下至上,从右至左调整结构
            adjustHeap(arr,i,arr.length);
        }
        //2.调整堆结构+交换堆顶元素与末尾元素
        for(int j=arr.length-1;j>0;j--){
            swap(arr,0,j);//将堆顶元素与末尾元素进行交换
            adjustHeap(arr,0,j);//重新对堆进行调整
        }

    }

    /**
     * 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
     * @param arr
     * @param i
     * @param length
     */
    public static void adjustHeap(int []arr,int i,int length){
        int temp = arr[i];//先取出当前元素i
        for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
            if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
                k++;
            }
            if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
                arr[i] = arr[k];
                i = k;
            }else{
                break;
            }
        }
        arr[i] = temp;//将temp值放到最终的位置
    }

    /**
     * 交换元素
     * @param arr
     * @param a
     * @param b
     */
    public static void swap(int []arr,int a ,int b){
        int temp=arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_25026989/article/details/89521473
今日推荐