常见算法 _待完善

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Main
{
    public class Calculate {
        #region 冒泡普通
        /*
         * 理解:依次取第1个到第N个数,和后面的数做比较,比他大则换位*/
        public static int[] BubbleSort(int[] arr, int len)
        {
            for (int i = 0; i < len; i++)//确定排序趟数,n趟
            {
                for (int j = i + 1; j < len; j++)//确定比较次数
                {
                    if (arr[i] > arr[j]) //交换
                    {
                        int temp = arr[i];
                        arr[i] = arr[j];
                        arr[j] = temp;
                    }
                }
            }
            return arr;
        }
        #endregion

        #region 冒泡加强版
        // 优化第四版 (鸡尾酒算法优化)
        /*
         * 设置了每一轮的循环边界,由于鸡尾酒算法是双向排序的,所以这里的边界也分别定义了左、右边界 leftBorder 和 rightBorder ,
         * 即每一轮循环都是以这两个边界为循环次数计算,相对于鸡尾酒第一版,第二版比较容易理解。
         * 鸡尾酒算法实现冒泡排序的优化确实可以很大程度上减少了比较的无用功,同时也要注意它的代码量也是之前的两倍。
         * 
         * 理解:正着比较换位,记录最后一个比较的index;倒着比较换位,记录最后一个比较的的index;不断缩小比较范围
         */
        public static int[] bubbleSort5(int[] arr, int len)
        {
            int temp = 0;
            bool isSorted = true;
            int lastLeftIndex = 0, lastRightIndex = 0;
            //左边界
            int leftBorder = 0;
            //右边界
            int rightBorder = arr.Length - 1;

            for (int i = 0; i < len / 2 - 1; i++)
            {
                // 奇数轮比较
                for (int j = leftBorder; j < rightBorder; j++)
                {
                    if (arr[j] > arr[j + 1])
                    {
                        temp = arr[j];
                        arr[j] = arr[j + 1];
                        arr[j + 1] = temp;
                        lastRightIndex = j;
                        isSorted = false;
                    }
                }
                rightBorder = lastRightIndex;
                if (isSorted)
                {
                    break;
                }

                // 偶数轮比较
                for (int j = rightBorder; j > leftBorder; j--)
                {
                    if (arr[j] < arr[j - 1])
                    {
                        temp = arr[j];
                        arr[j] = arr[j - 1];
                        arr[j - 1] = temp;
                        lastLeftIndex = j;
                        isSorted = false;
                    }
                }
                leftBorder = lastLeftIndex;
                if (isSorted)
                {
                    break;
                }
            }
            return arr;
        }
        #endregion

        #region 快速排序
        /*一、基本思想
            通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

          二、算法分析
            1、算法描述
                快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:

                从数列中挑出一个元素,称为 “基准”(pivot);
                重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
                递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
                首先我们给定一个数组 int[] arr = {1,28,3,21,11,7,6,18};*/
        public static int[] QuickSort(int[] arr, int low, int high)
        {
            if (low < high)
            {
                // 分区操作,将一个数组分成两个分区,返回分区界限索引
                int index = Partition(arr, low, high);
                // 对左分区进行快排
                QuickSort(arr, low, index - 1);
                // 对右分区进行快排
                QuickSort(arr, index + 1, high);
            }
            return arr;
        }

        public static int Partition(int[] arr, int low, int high)
        {
            int pivot = arr[low];
            while (low < high)
            {
                // 1、先看右边,依次往左递减
                while (pivot <= arr[high] && low < high)
                {
                    high--;
                }
                // 2、将右侧找到小于基准数的值加入到左边的(坑)位置, 左指针想中间移动一个位置
                if (low < high)
                {
                    arr[low] = arr[high];
                    low++;
                }
                // 3、再看左边,依次往右递增
                while (pivot > arr[low] && low < high)
                {
                    low++;
                }
                // 4、将左侧找到的打印等于基准值的值加入到右边的坑中,右指针向中间移动一个位置 high--
                if (low < high)
                {
                    arr[high] = arr[low];
                    high--;
                }
            }
            // 最后将基准为与low和high相等位置的数字交换
            arr[low] = pivot;
            // 返回基准值的位置索引
            return low;
        }
        #endregion

        #region 选择
        /*一、基本思想
            选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
        二、算法分析
            1、算法描述
            n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:
            初始状态:无序区为R[1…n],有序区为空;
            第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1…i-1]和R(i…n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1…i]和R[i+1…n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
            n-1趟结束,数组有序化了。
        */
        public static int[] SelSort(int[] arr)
        {
            for (int i = 0; i < arr.Length; i++)
            {
                // 最小数的索引
                int minIndex = i;
                // 找到最小数,并记录最小数的索引
                for (int j = i; j < arr.Length; j++)
                {
                    if (arr[j] < arr[minIndex])
                    {
                        minIndex = j;
                    }
                }
                int temp = arr[i];
                arr[i] = arr[minIndex];
                arr[minIndex] = temp;
            }
            return arr;
        }
        #endregion

        #region/*将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
        将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
        由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
        */
        public static int[] HeapSort(int[] arr)
        {
            // 构造初始堆(大顶堆),从第一个非叶子节点开始调整,左右孩子节点中较大的交换到父节点中
            for (int i = arr.Length / 2 - 1; i >= 0; i--)
            {
                heapAdjust(arr, i, arr.Length);
            }
            // 调整堆结构,交换堆顶元素与末尾元素
            for (int j = arr.Length - 1; j > 0; j--)
            {
                swap(arr, 0, j);// 将堆顶元素与末尾元素进行交换
                heapAdjust(arr, 0, j);// 重新对堆进行调整
            }
            return arr;
        }

        private static void swap(int[] arr, int a, int b)
        {
            int temp = arr[a];
            arr[a] = arr[b];
            arr[b] = temp;
        }

        // 调整大顶堆
        private static void heapAdjust(int[] arr, int i, int len)
        {
            int temp = arr[i], index = 2 * i + 1;
            while (index < len)
            {
                if (index + 1 < len && arr[index] < arr[index + 1])
                {// 如果左子结点小于右子结点,index指向右子结点
                    index += 1;
                }
                if (arr[index] > temp)
                {// 如果子节点大于父节点,将子节点值赋给父节点
                    arr[i] = arr[index];
                    i = index;
                    index = 2 * i + 1;
                }
                else
                {
                    break;
                }
            }
            arr[i] = temp;
        }
        #endregion

        #region 插入
        /*一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:
            从第一个元素开始,该元素可以认为已经被排序;
            取出下一个元素,在已经排序的元素序列中从后向前扫描;
            如果该元素(已排序)大于新元素,将该元素移到下一位置;
            重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
            将新元素插入到该位置后;
            重复步骤2~5。
        */
        public static int[] InsertionSort(int[] arr)
        {
            int preIndex, current;
            for (int i = 1; i < arr.Length; i++)
            {
                preIndex = i - 1;
                current = arr[i];
                while (preIndex >= 0 && arr[preIndex] > current)
                {
                    arr[preIndex + 1] = arr[preIndex];
                    preIndex--;
                }
                arr[preIndex + 1] = current;
            }
            return arr;
        }
        #endregion

        #region 希尔
        /*先取一个正整数 d1(d1 < n),把全部记录分成 d1 个组,所有距离为 d1 的倍数的记录看成一组,然后在各组内进行插入排序
            然后取 d2(d2 < d1)
            重复上述分组和排序操作;直到取 di = 1(i >= 1) 位置,即所有记录成为一个组,
            最后对这个组进行插入排序。一般选 d1 约为 n/2,d2 为 d1 /2, d3 为 d2/2 ,…, di = 1。
            列如:我们有一个数组[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ]如果我们以步长为5开始进行排序,我们可以通过将这列表放在有5列。
        */
        public static int[] ShellSort(int[] arr)
        {
            int gap = arr.Length / 2;
            int temp;
            while (gap >= 1)
            {
                for (int i = gap; i < arr.Length; i++)
                {
                    temp = arr[i];
                    int j = i - gap;
                    while (j >= 0 && arr[j] > temp)
                    {
                        arr[j + gap] = arr[j];
                        j -= gap;
                    }
                    arr[j + gap] = temp;
                }
                gap = gap / 2;
            }
            return arr;
        }
        #endregion

        #region 归并
        /*把长度为n的输入序列分成两个长度为n/2的子序列;
            对这两个子序列分别采用归并排序;
            将两个排序好的子序列合并成一个最终的排序序列
        */
        public static int[] MergeSort(int[] arr, int low, int high)
        {
            if (low < high)
            { //当子序列中只有一个元素时结束递归
                int mid = (low + high) / 2; //划分子序列
                MergeSort(arr, low, mid); //对左侧子序列进行递归排序
                MergeSort(arr, mid + 1, high); //对右侧子序列进行递归排序
                Merge(arr, low, mid, high); //合并
            }
            return arr;
        }

        private static void Merge(int[] arr, int low, int mid, int high)
        {
            int[] temp = new int[arr.Length]; //辅助数组
            int k = 0, i = low, j = mid + 1; //i左边序列和j右边序列起始索引,k是存放指针
            while (i <= mid && j <= high)
            {
                if (arr[i] <= arr[j])
                {
                    temp[k++] = arr[i++];
                }
                else
                {
                    temp[k++] = arr[j++];
                }
            }
            //如果第一个序列未检测完,直接将后面所有元素加到合并的序列中
            while (i <= mid)
            {
                temp[k++] = arr[i++];
            }
            //同上
            while (j <= high)
            {
                temp[k++] = arr[j++];
            }
            //复制回原数组
            for (int t = 0; t < k; t++)
            {
                arr[low + t] = temp[t];
            }
        }
        #endregion

    }

}

 

// See https://aka.ms/new-console-template for more information
using Main;
using System;


int[] arr = { 1, 5, 4, 69, 25, 45, 36, 8, 2, 9, 7 };

Console.WriteLine("冒泡");
//foreach (var item in Calculate.BubbleSort(arr, arr.Length))
//{
//    Console.WriteLine(item.ToString());
//}

Console.WriteLine("冒泡快速");
//foreach (var item in Calculate.bubbleSort5(arr, arr.Length))
//{
//    Console.WriteLine(item.ToString());
//}


Console.WriteLine("快速排序");
foreach (var item in Calculate.QuickSort(arr, 0, arr.Length - 1))
{
    Console.WriteLine(item.ToString());
}

Console.WriteLine("选择排序");
foreach (var item in Calculate.SelSort(arr))
{
    Console.WriteLine(item.ToString());
}

Console.WriteLine("堆排序");
foreach (var item in Calculate.HeapSort(arr))
{
    Console.WriteLine(item.ToString());
}

Console.WriteLine("插入");
foreach (var item in Calculate.InsertionSort(arr))
{
    Console.WriteLine(item.ToString());
}

Console.WriteLine("希尔");
foreach (var item in Calculate.ShellSort(arr))
{
    Console.WriteLine(item.ToString());
}

Console.WriteLine("归并");
foreach (var item in Calculate.MergeSort(arr, 0, arr.Length - 1))
{
    Console.WriteLine(item.ToString());
}






Console.ReadKey();

 

posted @ 2023-02-03 10:51  驼七  阅读(17)  评论(0)    收藏  举报