排序

 

选择排序

在一组数据中每次选最小的放在前面。第1趟找最小的,每找到一个数据比当前数据小,则两两交换,找到最小的后放在第1个位置,第2趟在剩下的数据中找最小的放在第2个位置,第i趟,在剩下的数据中找最小的放在第i个位置,直到找完整个数组。

由于我在每一次找最小时都是从前往后遍历,前一个循环找到的最小元素一定在后一次循环找到的最小元素前面,所以这是一个稳定的排序(当然每次从后往前找最小元素就可以改成不稳定的排序)

void selectSort(int *arr, int len)
{
    int tmp;
    for(int i = 0; i<len; i++)
    {
        for(int j = i+1; j<len; j++)
        {
            if(arr[j]<arr[i])
            {
                tmp = arr[j];
                arr[j] = arr[i];
                arr[i] = tmp;
            }
        }
    }
}

插入排序

如23 45 54 53  24

以23为第一个有序数组,往后遍历。到45时,比较45比前面有序数组的最后一个哪个大,由于45大于23,所以不用交换,继续向后遍历,同样方法继续向下遍历。遍历到53时,发现53比前面有序数组最后一个小,所以与54交换交换后顺序变为

23 45 53 54 24

然后比较53交换后与前面有序数组最后一个的值大小,若小于前面数组最后一个,则继续交换,然后继续与前面有序数组最后一个数值比较,知道遍历完

继续向后遍历,到下一个24,发现24比前一个有序数组的最后一个大,24与54交换。同样的方法一直与前面有序数组最后一个比较,直到值大于前面最后一个或者遍历完整个数组为止。

由于从后往前找当前比较数字与前面有序数组的最后一个值的关系,所以整个数组越有序,速度越快。时间复杂度最差O(n^2),最好O(n)。对于数值相同时,不交换顺序,所以是一个稳定的排序

void insertSort(int *arr,int len)
{
	int tmp;
	for(int i = 0; i<len-1; i++)
	{
		for(int j = i+1; j > 0; j--)
		{
			if(arr[j]>arr[j-1])
			{
				tmp = arr[j];
				arr[j] = arr[j-1];
				arr[j-1] = tmp;
			}
			else
			{
				break;
			}
		}
	}
}

希尔排序

希尔排序是基于插入排序的一种优化。首先将数据分组,如将20 21 0 7 23五个数据按2个数据分一组,在排序时两个数选一个进行插入排序,比如选第0个数“20”,第2个数“0”,第4个数“23”,进行插入排序。先将0与20比较,0大于20,两两对换,此时变成0 21 20 7 23,再将23与20比较,若后者小于前者,两两对换,保证前面小于后面,然后就做到了每组中选出的一个数有序。同样的找“1”“3”号下标的数,做比较就得到了 0 7 21 20 23。然后再将数据按4个数一组,同样的办法保证各个组选出的数有序。让后再加每组的数,直到所有数一组,这个时候就变成了完全的插入排序。

希尔排序由于不同分组可能会导致相同值的顺序改变,所以属于不稳定的排序。时间复杂度O(n^1.3)到O(n^1.5)之间。

其中,每次到底分2个,4个,8个一组或者其他的分法有待数学验证,这里只是写代码方便就写成了这个值

void shellSort(int *arr,int len)
{
    int tmp;
    for(int gap = len/2; gap>0; gap/=2)
    {
        for(int i = gap; i<len; i++)
        {
            for(int j = i; j-gap >= 0&&arr[j] < arr[j-gap]; j-=gap)
            {
                tmp = arr[j];
                arr[j] = arr[j-gap];
                arr[j-gap] = tmp;
            }
        }
    }
}

快速排序

顾名思义,这个排序的方法是快,并且数据越乱越快(相比较其他排序),算法时间复杂度nlogn。

基本排序规则是每一趟从数列中选取一个数拿出来,定一个首标记和一个尾巴标记。尾标记往前走,找到比拿出数据大的数放在后面,首标记往后走,比拿出数小的数放在前面,当首尾标记重合时返回这个位置,一趟排序结束。然后再将分开的两段分别用这种方法排序。所排的区间会来越小,直到所有要排的区间数为1时排序结束。

int sortOnly(int *arr,int low,int high)
{
    int tmp = arr[low];//拿出low下标数据作为本次排序的参照
    while(low < high)
    {
        while(low < high&&arr[high] >= tmp)//每次从右向左找到比tmp小的数的下标high
        {
            high--;
        }
        if(low < high)//对于不满足arr[high] >= tmp的,用找到的比tmp小的数将low位置数覆盖。然后low往后走一步,保证low前面的数比tmp小
        {
            arr[low++] = arr[high];
        }
        else//不满足low < high的条件退出循环,本次排序结束
        {
            break;
        }
        while(low < high&&arr[low] <= tmp)//从左向右找比tmp大的数的下标low
        {
            low++;
        }
        if(low<high)//对于不满足arr[low] <= tmp的,用找到的比tmp大的数将high位置数覆盖。然后high往前走一部,保证high后面的数比tmp大
        {
            arr[high--] =  arr[low];
        }
        else
        {
            break;
        }
    }
    arr[low] = tmp;
    return low;
}

void quick(int *arr,int low,int high)
{
    int sit = sortOnly(arr,low,high);//局部排序
    
    if(sit-1 > low)//若sit与low相隔至少两个数据,再次排序
    {
        quick(arr,low,sit-1);
    }
    if(sit+1 < high)//若sit与high相隔至少两个数据,再次排序
    {
        quick(arr,sit+1,high);
    }
}

void quickSort(int *arr,int len)
{
    assert(NULL != arr);//断言传入数组有效
    quick(arr,0,len-1);//将传入数组首尾下标作为参数传入
}

上面的方式虽然完成了快排的思路,但是由于quick函数每次递归调用,空间复杂度太大,我们利用栈将递归改为非递归。

quickStack(int *arr,int low,int high)
{
    std::stack<int> sta;
    sta.push(low);
    sta.push(high);
    while(!sta.empty())
    {
        high = sta.top();
        sta.pop();
        low = sta.top();
        sta.pop();
        sit = sortOnly(arr,low,high);
        if(sit-1>low)
        {
            sta.push(low);
            sta.push(sit-1);
        }
        if(sit+1<high)
        {
            sta.push(sit+1);
            sta.push(high);
        }
    }
}

堆排序

堆排序的原理是将数据看做堆。如整形数组arr[0] = {23,53,55,65,27}。我们将数组看作下图的堆

每个数据满足下列性质。

若根的下标为i,则他的左孩子下标为i*2+1,右孩子下标为i*2+2。如53下标为1,左孩子65下标为3,右孩子27下标为4;

若叶的下标为i,则他的直接双亲为(i-1)/2。如53下标为1,双亲23下标为0

堆排序完成的任务是将这个堆调整为小根堆或大根堆

小根堆(根数值最小,兄弟节点左比右小);

大根堆(根数值最大,兄弟节点左比右大);

在这里演示简历大根堆。基本思路是先局部调整。将一个局部的堆调整成根最大,比如将上图的53,65,27。如下图所示

用同样的方法,将数组最后到起始,从后向前一直调整,就能将整个数组堆的最顶端数调整为最大值。

然后再将堆顶的数与最后一个数交换。再次进行调整,这次调整的数组不包括最后一个数。反复调整,再将调整后堆顶数与当前调整的堆最后一个数交换。最终就能调整为从后到前数据依次减小了。

void heapOnly(int *arr,int parent,int len)
{
    if(len < 2)
    {
        return ;
    }

    int tmp = arr[parent];
    for(int i = parent*2+1; i<len; i = i*2+1)
    {
        if(i+1 < len&&arr[i] < arr[i+1])
        {
            i++;
        }
        if(arr[i] > tmp)
        {
            arr[parent] = arr[i];
            parent = i;
        }
        else
        {
            break;
        }
    }
    arr[parent] = tmp;
}

void heapSort(int *arr,int len)
{
    assert(NULL != arr);
    for(int i = (len-1-1)/2; i>=0; len--)
    {
        heapOnly(arr, i, len);
    }
    int tmp;
    for(int i = 0; i<len; i++)
    {
        tmp = arr[0];
        arr[0] = arr[len-1-i];
        arr[len-1-i] = tmp;
        heapOnly(arr, i, len-1-i);
    }
}

冒泡排序

从前到后遍历,每连续两个数进行比较,大的放在后面,遍历完一次整个数组,最小的数就在最后面。下一次将不包括最后一个数的数组进行同样的遍历,找出第二大的放在倒数第二,以此循环完成从小到大排序

void bubbleSort(int *arr,int len)
{
    int tmp;
    for(int i = 0; i<len; i++)
    {
        for(int j = 0; j<len-i-1; j++)
        {
            if(arr[j]>arr[j+1])
            {
                tmp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = tmp;
            }
        }
    }
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值