版权声明:本文为博主原创文章,未经博主允许不得转载。 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;
}
}