zoukankan      html  css  js  c++  java
  • 排序算法

    快速排序(基于交换)

    特性

    每次使一个数归位。不稳定

    思想

    每次partition划分数组为两部分并返回下标。随后递归地对左右序列快速排序。

    代码

    int Partition(int arr[],int left,int right)
    {
    	int i = left;
    	int j = right;
    	int pivotindex = left; //基准坐标可以任意选
    	int pivotnum = arr[pivotindex]; //基准数
    	cout << "pivotnum = " << pivotnum << endl;
    	while(i < j) //当i==j的时候,i和j的下标就是基准数的正确位置。
    	{
    		while (i < j && arr[j] >= pivotnum) //先从后面往前找小于基准数
    			j--;
    		arr[i] = arr[j];   //复制到前面i的位置,不用交换
    		while (i < j && arr[i] <= pivotnum)  //再从前往后找大于基准数的值
    			i++;
    		arr[j] = arr[i]; //复制到后面j的位置。
    	}
    	arr[i] = pivotnum;  //i,j的位置就是要找的基准数所在的位置。至此数组划分完毕。
    	return i;
    }
    void QuickSort(int arr[], int left,int right)
    {
    	if (left == right)
    		return;
    	
    		int index = Partition(arr, left, right);
    		if(index>left)
    		QuickSort(arr, left, index - 1);
    		if(index<right)
    		QuickSort(arr, index + 1, right);
    	
     }
    

    冒泡排序(交换排序)

    思想:

    两两比较,每次使最小的元素不断往上浮。

    特点:

    时间复杂度O(N)~O(N^2),空间复杂度为O(1)。稳定,每趟排序使得一个最小的元素归位,全局有序。

    代码:

    oid bubbleSort(int arr[], int len)
    {
    	for (int i = 0; i < len-1; i++)
    	{
    		int flag = 0;//flag记录此次排序有没有交换数字
    		for (int j = len - 1; j >= i+1; j--)//从后往前冒泡
    		{
    			if (arr[j] < arr[j - 1])
    			{
    				swap(arr[j], arr[j - 1]);
    				flag = 1;
    			}
    		}
    		if (flag == 0) //如果本次排序没有交换数字,则表示全局有序。
    			return;
    	}
    }
    

    直接插入排序(插入排序)

    思想:

    将后面无序数组中的一个待排序的数插入到前面已排序的序列中去。找一个无序的数字,在前面有序的序列中找到插入的位置,移动元素,插入。

    特点:

    稳定,适用于链表。时间复杂度 O(N)~O(N^2),空间复杂度:O(1)。适用于基本有序或规模小的数组。

    代码:

    void InsertSort(int arr[], int len)
    {
    	int i, j;
    	for (i = 1; i < len; i++) //从第二个数字开始往前插入。
    	{
    		int temp;  //保存将要往前插入的数字。
    		if (arr[i] < arr[i - 1])//若这个数字本身和前面有序数组放一起是有序的,则跳过。
    		{
    			j = i;
    			temp = arr[i];
    			for (; j>0&&temp < arr[j-1]; j--) //从后面往前找插入的位置
    			{
    				arr[j] = arr[j-1]; //直接覆盖
    			}
    			arr[j] = temp;
    		}
    	}
    }
    

    折半插入排序(插入排序)

    思想:

    相比于直接插入排序,折半插入排序将比较和移动操作分离。查找插入的位置用二分法。

    特点:

    减少了比较的次数,但移动的次数没变,时间复杂度仍未O(N^2)。

    代码:

    void InsertSort2(int arr[], int len)
    {
    	int i, j;
    	for (i = 1; i < len; i++)
    	{
    		int temp;
    		if (arr[i] < arr[i - 1])
    		{
    			
    			temp = arr[i];
    			int low = 0;
    			int high = i - 1;
    			
    			while (low <=high)
    			{
    				int mid = (low + high) / 2;
    				if (arr[i] > arr[mid])
    				{
    					low = mid + 1;
    				}
    				else if (arr[i] < arr[mid])
    				{
    					high = mid - 1;
    				}
    			}
    			for (j = i-1; j >= high+1 ; j--)
    			{
    				arr[j + 1] = arr[j];
    			}
    			arr[high + 1] = temp;
    		}
    	}
    }
    

    希尔排序(插入排序)

    思想:

    分为若干子表分别直接插入排序,最后当总体有序时进行一次直接插入排序。步长逐步减少。不能用于链表。

    特点:

    时间复杂度:O(N^1.3)(取决于选择的序列),时间复杂度:O(1)。不稳定,不能用于链表。

    代码:

    void ShellSort(int arr[], int len)
    {
    	for (int gap = len / 2; gap >= 1; gap = gap / 2) //步长逐渐减小
    	{
    		for (int i = gap; i < len; i++) //并不需要排完一个子序列再去排下一个子序列。
    		{
    			if (arr[i] < arr[i - gap]) //无序,说明需要插入到前面的有序数组中
    			{
    				int temp = arr[i];
    				int j;
    				for (j = i - gap; j >= 0 && temp < arr[j]; j = j - gap)//找插入位置
    					arr[j + gap] = arr[j]; //移动元素。
    				arr[j + gap] = temp;
    			}
    		}
    	}
    }
    

    简单选择排序:

    思想:

    每次在待排序列表中选一个最小的排到前面去。

    特点:

    与初始序列无关,时间复杂度始终为O(N^2),空间复杂度为O(1)。不稳定.移动次数小,但是比较次数不变。

    代码:

    void selectsort(int arr[], int len)
    {
    	for (int i = 0; i < len - 1; i++)//待排序的数组大小越来越小
    	{
    		int min = i;  //保存最小数的下标
    		for (int j = i + 1; j < len; j++)
    		{
    			if (arr[j] < arr[min])
    				min = j;
    			
    		}
    		if (min != i)
    			swap(arr[i], arr[min]); //交换数字
    	}
    }
    

    堆排序 (选择排序)

    思想:按照层次遍历的顺序标号映射到数组中。

    大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
    小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
    将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
    建立初始堆,输出堆顶元素,将堆底元素送入堆顶,向下调整。

    特点:

    建堆时间为O(N),之后又n-1次向下调整,每次为O(logN),所以时间复杂度始终为O(NlogN)。不稳定。
    删除堆顶元素:把它与堆的最后一个元素交换,向下调整。
    插入操作:把新的节点放到堆的末尾,向上调整。

    代码:

    void adjust(int arr[], int len, int index)
    {
        int left = 2 * index + 1; // index的左子节点
        int right = 2 * index + 2;// index的右子节点
    
        int maxIdx = index;
        if (left<len && arr[left] > arr[maxIdx])     maxIdx = left;
        if (right<len && arr[right] > arr[maxIdx])     maxIdx = right;  找左右子节点最大的数交换
    
        if (maxIdx != index)  //如果将要发生交换
        {
            swap(arr[maxIdx], arr[index]);
            adjust(arr, len, maxIdx);
        }
    
    }
    
    // 堆排序
    void heapSort(int arr[], int size)
    {
        // 构建大根堆(从最后一个非叶子节点向上)
        for (int i = size / 2 - 1; i >= 0; i--)
        {
            adjust(arr, size, i);
        }
    
        // 调整大根堆
        for (int i = size - 1; i >= 1; i--)
        {
            swap(arr[0], arr[i]);           // 将当前最大的放置到数组末尾
            adjust(arr, i, 0);              // 将未完成排序的部分继续进行堆排序
        }
    }
    

    归并排序

    特性

    时间复杂度一直是O(nlogn),稳定,需要辅助数组

    思想

    采用分治的思想,将数组分为两段,分别归并排序,再合并。

    代码

    void Merge(int arr[], int l, int mid, int r)  //合并两个数组
    {
    	int* help = new int[r - l + 1];  //辅助数组
    	int p1 = l;   
    	int p2 = mid + 1;
    	int index = 0;
    	while (p1 <= mid && p2 <= r)
    	{
    		help[index++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
    	}
    	while (p1 <= mid)  //复制剩余的元素
    	{
    		help[index++] = arr[p1++];
    	}
    	while (p2 <= r)
    	{
    		help[index++] = arr[p2++];
    	}
    	for (int i = 0; i < r - l + 1; i++) //复制到原数组
    	{
    		arr[l+i] = help[i];
    	}
    	delete[] help;
    }
    void MergeSort(int arr[], int l, int r)
    {
    	if (l == r)
    		return;
    	if (l < r)
    	{
    		int mid = (l + r) / 2;
    		MergeSort(arr, l, mid);
    		MergeSort(arr, mid+1, r);
    		Merge(arr, l, mid, r);
    	}
    	
    }
    

    基数排序

    思想:

    有LSD(最高位优先),MSD(最高位优先),先按某位数的不同分配到桶中,再收集起来,接着再按下一位分配和收集。

    特点:

    不依靠比较来排序,稳定。时间复杂度O(d(n+radix))。

  • 相关阅读:
    Windows环境下多线程编程原理与应用读书笔记(7)————事件及其应用
    Windows环境下多线程编程原理与应用读书笔记(6)————临界段及其应用
    Windows环境下多线程编程原理与应用读书笔记(5)————互斥及其应用
    [bzoj1910] [Ctsc2002] Award 颁奖典礼
    [bzoj4411] [Usaco2016 Feb]Load balancing
    [bzoj2654] tree
    [bzoj4410] [Usaco2016 Feb]Fence in
    [bzoj3143] [Hnoi2013]游走
    [bzoj1826] [JSOI2010]缓存交换
    [bzoj1483] [HNOI2009]梦幻布丁
  • 原文地址:https://www.cnblogs.com/void-lambda/p/12379954.html
Copyright © 2011-2022 走看看