算法:排序

排序

排序的基本概念与术语

  • 排序:把一组记录按照某个域值的递增或递减次序重新排列的过程
  • 主关键字:能唯一标识某一记录的关键字
  • 稳定性:具有相同关键字的记录,排序前后保持它们原来的相对次序不变,则称该排序过程具有稳定性
    在这里插入图片描述
  • 内部排序:排序过程都在内存中进行的排序
  • 外部排序:排序过程需要在内存和外存之间不断交换数据的排序

插入排序

直接插入排序

  • 思想:将一个记录插入到已经排好序的有序表中,从而得到一个新的记录增一的有序表
    在这里插入图片描述
  • 编程实现
        public static void StraightInsertSort<T>(T[] keys) where T : IComparable<T>
        {
            for(int i = 1;i < keys.Length; i++) 
            {
                T current = keys[i];
                int j = i - 1;
                while (j >= 0 && keys[j].CompareTo(current) > 0)
                {
                    keys[j + 1] = keys[j];
                    j--;
                }
                keys[j + 1] = current;
            }
        }
        static void Main(string[] args)
        {
            int[] nums = new int[] { 45, 34, 78, 12, 34, 32, 29, 64 };
            Sort.StraightInsertSort(nums);
            foreach(int num in nums)
            {
                Console.WriteLine(num);
            }
        }
//
12
29
32
34
34
45
64
78

希尔插入排序

  • 思想:把记录按下标的一定增量分组,对每组使用直接插入排序法,随着增量逐渐减少,所分组包含的记录越来越多,到增量值减至1时,整个记录集被分成一组,算法终止。(先将整个待排序列分割成为若干子序列,分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序)
  • 操作:(1).选择一个增量序列(2).按增量序列个数k,对序列进行k趟排序(3).每趟排序,根据对应的增量,将待排序列分割成若干长度为m的子序列,分别对各子表进行直接插入排序,当增量因子为1时,整个序列作为一个表来处理,表长度为整个序列的长度
    在这里插入图片描述
  • 大的往后移动,小的往前移动
  • 编程实现
        private static void Shell<T>(int delta,T[] keys) where T : IComparable<T> 
        {
            for(int i = delta;i < keys.Length; i++) 
            {
                T current = keys[i];
                int j = i - delta;
                while(j >= 0 && keys[j].CompareTo(current) > 0) 
                {
                    keys[j + delta] = keys[j];
                    j -= delta;
                }
                keys[j + delta] = current;
            }
        }
        public static void ShellInsertSort<T>(T[] keys)where T : IComparable<T> 
        {
            for(int delta = keys.Length / 2;delta > 0;delta = delta / 2) 
            {
                Shell(delta, keys);
            }
        }
        static void Main(string[] args)
        {
            int[] nums = new int[] { 45, 34, 78, 12, 34, 32, 29, 64 };
            Sort.ShellInsertSort(nums);
            foreach(int num in nums)
            {
                Console.WriteLine(num);
            }
        }
//
12
29
32
34
34
45
64
78

选择排序

直接选择排序

  • 思想:每一趟在n-i个记录中选取关键字最小的记录作为有序序列中第i个记录。(在要排列的一组数中,选出最小的一个数与第1个位置的数交换;然后在剩下的数当中再找最小的与第2个位置的数交换,依此类推,直到第n-1个元素和第n个元素比较为止)
    在这里插入图片描述
  • 代码实现
        public static void StraightSelectSort<T>(T[] keys)where T : IComparable<T> 
        {
            for(int i = 0;i < keys.Length - 1; i++) 
            {
                T current = keys[i];
                int k = i;//记录最小值位置
                for(int j = i + 1;j < keys.Length; j++) 
                {
                    if(keys[j].CompareTo(current) < 0) 
                    {
                        current = keys[j];
                        k = j;
                    }
                }
                if(k != i) 
                {
                    T temp = keys[i];
                    keys[i] = current;
                    keys[k] = temp;
                }
            }
        }
        static void Main(string[] args)
        {
            int[] nums = new int[] { 45, 34, 78, 12, 34, 32, 29, 64 };
            Sort.StraightSelectSort(nums);
            foreach(int num in nums)
            {
                Console.WriteLine(num);
            }
        }
//
12
29
32
34
34
45
64
78

堆排序

  • 堆排序是以重树形选择排序,是对直接选择排序的有效改进
  • 堆的概念:具有n个元素的序列K={k1,k2,…,kn},当且仅当满足k1<=k2i,ki<=k2i+1或者k1>=k2i,ki>=k2i+1(树形的左右孩子节点)i=1,2,...,[n/2]时称之为堆
  • 若关键码的集合K={k1,k2,…,kn},把它们按照完全二叉树的顺序存放在一维数组中,若满足k1<=k2i,ki<=k2i+1称为小根堆,若满足k1>=k2i,ki>=k2i+1称为大根堆
  • 小根堆与大根堆的例子
    在这里插入图片描述
  • 思想:以构建大根堆为例,构建大根堆之后,输出堆顶记录,对剩余的n-1个记录接着构建大根堆,便可得到n个记录的次大值,如此反复执行,就能得到一个有序序列,这个过程称为堆排序(注:编号要从1开始)
  • 堆排序需要解决的问题:
  • 问题1:如何构建堆,对初始序列建堆的过程就是一个反复进行筛选的过程,(1).n个结点的完全二叉树,则最后一个结点是第[n/2]个结点的子树.(2).筛选从第[n/2]个结点为根的子树开始,该子树成为堆.(3).之后向前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点(即把序号为[n/2],[n/2]-1,…1的记录作为根的子树调整为堆)
  • 问题2:输出堆顶元素后,进行调整新堆
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 代码实现
        private static void Restore<T>(T[] keys,int j,int vCount) where T : IComparable<T>
        {
            while(j <= vCount / 2) 
            {
                int index = 2 * j + 1 <= vCount && keys[2 * j + 1].CompareTo(keys[2 * j]) > 0 ? 2 * j + 1 : 2 * j;
                if(keys[j].CompareTo(keys[index]) < 0) 
                {
                    T temp = keys[j];
                    keys[j] = keys[index];
                    keys[index] = temp;

                    j = index;
                }
                else 
                {
                    break;
                }
            }
        }
        public static void HeapSelectSort<T>(T[] keys) where T : IComparable<T>
        {
            int vCount = keys.Length;
            T[] keys1 = new T[vCount + 1];
            for (int i = 0; i < vCount; i++)
            {
                keys1[i + 1] = keys[i];//序号从1开始
            }
            for (int i = vCount / 2; i >= 1; i--)
            {
                Restore(keys1, i, vCount);
            }
            for (int i = vCount; i >= 2; i--) 
            {
                T temp = keys1[1];
                keys1[1] = keys1[i];
                keys1[i] = temp;

                Restore(keys1, 1, i - 1);//从顶部实现
            }
            for(int i = 0;i < vCount; i++) 
            {
                keys[i] = keys1[i + 1];//再换回去
            }
        }
        static void Main(string[] args)
        {
            int[] nums = new int[] { 45, 34, 78, 12, 34, 32, 29, 64 };
            Sort.HeapSelectSort(nums);
            foreach(int num in nums)
            {
                Console.WriteLine(num);
            }
        }
//
12
29
32
34
34
45
64
78

交换排序

冒泡排序

  • 通过相邻记录之间的比较和交换,使关键字较小的记录如气泡一般逐渐向上漂移直至水面
  • C#代码实现
        public static void BubbleExchangeSort<T>(T[] keys) where T : IComparable<T>
        {
            for(int i = 0;i < keys.Length - 1; i++) 
            {
                for(int j = keys.Length - 1;j > i; j--) 
                {
                    if(keys[j].CompareTo(keys[j - 1]) < 0) 
                    {
                        T temp = keys[j];
                        keys[j] = keys[j - 1];
                        keys[j - 1] = temp;
                    }
                }
            }
        }
        public static void BubbleExchangeSortImprove<T>(T[] keys) where T : IComparable<T>
        {
            for (int i = 0; i < keys.Length - 1; i++)
            {
                bool flag = false;
                for (int j = keys.Length - 1; j > i; j--)
                {
                    if (keys[j].CompareTo(keys[j - 1]) < 0)
                    {
                        T temp = keys[j];
                        keys[j] = keys[j - 1];
                        keys[j - 1] = temp;
                        flag = true;
                    }
                }
                if (flag == false)
                    break;//利用flag进行改进看是否已经排好了序
            }
        }
  • Java代码实现

快速排序

  • 基本思想:(1).选择一个基准元素,通常选择第一个元素或者最后一个元素。(2).通过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小,另一部分记录的元素值比基准值大。(3).此时基准元素在其排好序的正确位置。(4).然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序,即:从待排记录中选一记录,将其放入正确的位置,然后以该位置为界,对左右两部分再做快速排序,直到划分的长度为1

ex1

int[] Key = new int[]{45,34,78,12,34,32,29,64};
  • 先从45为基准值开始确定45应处的位置,i指向45,j指向64,因为64大于45所以j往前移动指向2929小于45,j停止,观察34发现45大于34所以i右移动,移动到78发现比45大,因为我们确定基准元素所在的位置后以该位置为界左边是小于基准元素的区间右边是大于基准元素的区间,而i所处的值78大于45j所处的值29小于45,所以7829要交换位置
{45,34,29,12,34,32,78,64}
  • 之后j指向78继续前移到32发现3245小停止移动i往后移1245小,再往后3445小,在往后移ij相遇,所以就保证了i之前的位置比45小,i``之后的位置比45大,所以找到45所在的位置,并将3245交换得到数组为
{32,34,29,12,34,45,78,64}
  • 这样就得到了两个序列,45的左边32,34,29,12,3445小,45的右边78,6445大,之后再对这两个序列继续进行快速交换排序,利用递归思想,对于左边的序列32,34,29,12,34利用同样的方法以32为基准元素,i指向32,j指向3434比基准元素大,j前移,发现12比基准元素小,j停止,i往后移,3432大,所以i停止,交换3412,重复刚才的过程就确定了这个序列利用快排后的结果是29,12,32,34,34在以基准元素32为界分成两个区间29,1234,34,再对29,12进行快排后得到基准元素29的位置为12,29再以29为界限左边只有一个元素右边没有元素,这样就确定好了,再去确定右边的区间34,34这里i指向第一个34j指向第二个34,因为j所指向的元素不比34大所以j停止,i向后移动发现34<=34,所以i向后移动,就找到了基准元素34(第一个34)应处的位置,再分为两部分左边只有一个元素右边没有元素,就排好序了,之后对78,64采取同样的方法即可(递归思想注意顺序),最后就可以得到排好序的数组
  • 步骤图
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 编程实现
        public static void QuickExchangeSort<T>(T[] keys)where T : IComparable<T> 
        {
            QuickSort(keys, 0, keys.Length - 1);
        }
        private static void QuickSort<T>(T[] keys,int left,int right) where T : IComparable<T>
        {
            if(left >= right) 
            {
                return;
            }
            int i = left;
            int j = right;
            T current = keys[left];
            while(i < j) 
            {
                while(keys[j].CompareTo(current) > 0 && i < j) 
                {
                    j--;
                }//j前移
                while(keys[i].CompareTo(current) <= 0 && i< j) 
                {
                    i++;
                }//i后移
                if(i < j) 
                {
                    T temp = keys[i];
                    keys[i] = keys[j];
                    keys[j] = temp;
                }//发生i指向的位置比基准元素大,j指向的位置比基准元素小,需要交换元素
            }
            keys[left] = keys[j];
            keys[j] = current;//找到基准元素所在的位置进行交换
            if (j - 1 > left) QuickSort(keys, left, j - 1);//分成左右两个区间继续进行快速交换排序
            if (j + 1 < right) QuickSort(keys, j + 1, right);
        }
        static void Main(string[] args)
        {
            int[] nums = new int[] { 45, 34, 78, 12, 34, 32, 29, 64 };
            Sort.QuickExchangeSort(nums);
            foreach(var num in nums)
            {
                Console.WriteLine(num);
            }
        }
//
12
29
32
34
34
45
64
78

合并排序

  • 思想:把两个或多个有序序列合并成一个有序序列
  • 代码实现
        public static T[] MergeSort<T>(T[] keys1, T[] keys2) where T : IComparable<T>
        {
            T[] result = new T[keys1.Length + keys2.Length];
            int i = 0, j = 0, k = 0;
            while (i < keys1.Length && j < keys2.Length)
            {
                if (keys1[i].CompareTo(keys2[j]) < 0)
                {
                    result[k] = keys1[i];
                    i++;
                }
                else
                {
                    result[k] = keys2[j];
                    j++;
                }
                k++;
            }
            while (i < keys1.Length)
            {
                result[k] = keys1[i];
                k++;
                i++;
            }
            while(j < keys2.Length) 
            {
                result[k] = keys2[j];
                k++;
                j++;
            }
            return result;
        }

对于一个无序序列如何利用合并排序进行实现呢?
答案:使用递归思想,首先将无序序列利用二分方式进行打散,之后再合并起来,并把排好序的序列赋给原数组即可

        public static void MergeSort<T>(T[] keys) where T : IComparable<T> 
        {
            MergeSort(keys, 0, keys.Length - 1);
        }
        private static void MergeSort<T>(T[] keys,int left,int right) where T : IComparable<T>
        {
            if (left >= right)
                return;
            int mid = (left + right) / 2;
            MergeSort(keys, left, mid);//向左切
            MergeSort(keys, mid + 1, right);//对序列进行打散
            MergeSort(keys, left, mid, right);
        }
        private static void MergeSort<T>(T[] keys,int left,int mid,int right)where T : IComparable<T> 
        {
            T[] result = new T[right - left + 1];
            int i = left;
            int j = mid + 1;//第二个序列的头
            int k = 0;
            while(i <= mid && j <= right) 
            {
                if(keys[i].CompareTo(keys[j]) < 0) 
                {
                    result[k] = keys[i];
                    i++;
                }
                else 
                {
                    result[k] = keys[j];
                    j++;
                }
                k++;
            }
            while(i <= mid) 
            {
                result[k] = keys[i];
                i++;
                k++;
            }
            while(j <= right) 
            {
                result[k] = keys[j];
                j++;
                k++;
            }
            for(int n = 0;n < result.Length; n++) 
            {
                keys[left + n] = result[n];//把排序好的赋回去
            }
        }
        static void Main(string[] args)
        {
            int[] nums = new int[] { 45, 34, 78, 12, 34, 32, 29, 64 };
            Sort.MergeSort(nums);
            foreach(var num in nums)
            {
                Console.WriteLine(num);
            }
        }
//
12
29
32
34
34
45
64
78
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值