排序算法(C# and Lua)

概述

十大经典排序算法分别是冒泡排序插入排序选择排序希尔排序计数排序基数排序桶排序快速排序归并排序堆排序

可以分为两大类:

比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。

非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。

冒泡排序

(Bubble Sort)

是一种交换排序,通过两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。

C#实现

 public static void Swap(ref int num1,ref int num2)
 {
     int temp = num1;
     num1 = num2;
     num2 = temp;
 }

//从小到大排序
public static void BubbleSort(int[] array)
{
    //循环判断的趟数  进行N-1趟排序
    for (int i = 1; i < array.Length; i++)
    {
       bool canSwap = false; 

        //从第一个元素进行遍历比对,如果前一个元素大于后一个元素,就进行交换
       for(int j = 0;j< array.Length-i; j++) 
       { 
          if (array[j] > array[j + 1])
            {
                Swap(ref array[j], ref array[j + 1]);
                canSwap = true; 
            }
       }

       //判断元素比较时是否有交换操作,如果没有则说明序列已排序,跳出循环
       if(!canSwap)
       {
            break;
       }
    }
}

Lua实现

local Print = function(list)
   local str = ""
   for k,v in ipairs(list)do
      str = str..v.."  "
   end 
   print(str)
end


function BubbleSort(arr)
    --判断的趟数  (n-1次)
    for i = 1,#arr-1 do
       --判断在一趟排序中是否有进行过交换操作
       local isSwap = false   
       --在改趟排序中进行比较
       for j = 1,#arr-i do
         print(j)
          if arr[j] > arr[j+1] then
             arr[j],arr[j+1] = arr[j+1],arr[j]
             isSwap = true
          end  
       end  
       --如果没有进行交换操作,说明已排好序
       if not isSwap then
          break
       end  
    end  
end  

local numList = { 10, 8, 11, 7, 4, 12, 9, 6, 5, 3 ,0,2,1,2};

BubbleSort(numList)
Print(numList)

简单选择排序

(Simple Selection Sort)

1、对长度为n的序列进行n-1趟排序,每次从待排序列中选出最小的元素,并与待排序列中的第一个元素交换

2、不稳定排序

 C#实现

public void SimpleSelectionSort(int[] arr)
{
    int minIndex;//待排序列中最小值索引
    int i, j;
    //从第一个值开始到倒数第二个数,从该值之后的序列中找出最小值
    for (i = 0; i < arr.Length - 1; i++)
    {
        //将待排序列的第一个值的索引暂定为最小值索引
        minIndex = i;
        for (j = i+1; j < arr.Length; j++)
        {
            //从待排序列中找出最小值索引
            if (arr[j] < arr[minIndex])
            {
                minIndex = j;
            }
        }

        if (minIndex != i)
        {
            //将最小值与待排序列的第一个值进行交换
            (arr[minIndex], arr[i]) = (arr[i], arr[minIndex]);
        }
    }
}

Lua实现

local Print = function(list)
   local str = ""
   for k,v in ipairs(list)do
      str = str..v.."  "
   end 
   print(str)
end

--简单选择排序
function SimpleSelectionSort(arr)
   local minIndex;--最小值索引
   --从第一个值开始到倒数二个数,从该数之后的序列中找出最小值
   for i = 1,#arr-1 do
      minIndex = i
      for j = i+1,#arr do
         if arr[j] < arr[minIndex] then
            minIndex = j
         end   
      end   

      if minIndex ~= i then
         --将最小值与待排序列中的第一个数进行交换
         arr[i],arr[minIndex] = arr[minIndex],arr[i]
      end   

   end   
end  

local numList = { 10, 8, 11, 7, 4, 12, 9, 6, 5, 3 ,0,2,1,2};

SampleSelectionSort(numList)
Print(numList)

直接插入排序

(Straight Insertion Sort)

将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表。

 C#实现

public static void PrintList(int[] array)
{
    for (int i =0;i<array.Length; i++)
    {
        Console.Write(array[i]+"  ");    
    }
    Console.WriteLine("\n");
}

public static void Swap(ref int num1,ref int num2)
{
    int temp = num1;
    num1 = num2;
    num2 = temp;
}


public static void StraightInsertSort(int[] array)
{
    for(int i = 1;i< array.Length; i++)
    {
         int key = array[i];
         int j = i - 1;
         while (j >= 0 && array[j] > key)
         {
             array[j + 1] = array[j];
             j--;
         }
         array[j + 1] = key;
    }
}

Lua实现

--直接插入排序
local SampleInsertSort = function(list)
   for i = 2,#list do
      local key = list[i]
      local j = i-1
      while(j>=1 and list[j] > key) do
         list[j+1] = list[j]
         j = j - 1
      end   
      list[j+1] = key
   end   
end   

local numList = { 10, 8, 11, 7, 4, 12, 9, 6, 5, 3 ,0,2,1,2};

SampleInsertSort(numList)
Print(numList)

希尔排序

(Shell Sort)

又称缩小增量排序,设定一个增量,将序列中的记录通过该增量进行分组,在组内进行直接插入排序。不断缩小增量,直到最后进行一次增量为1的直接插入排序。

C#实现

public void ShellSort(int[] arr)
{
    //计算出所有增量情况
    for (int step = arr.Length / 2; step >= 1; step /= 2)
    {
        //处理各增量的分组
        for (int group = 0; group < step; group++)
        {
            //对该分组进行直接插入排序
            for (int i = group + step; i < arr.Length; i += step)
            {
                int value = arr[i];
                int j = i - step;
                while (j >= 0 && arr[j] > value)
                {
                    arr[j+step] = arr[j];
                    j -= step;

                }
                arr[j + step] = value;
            }
        }
    }
}

Lua实现

function ShellSort(arr)
    --计算所有增量情况
    local step = math.modf(#arr/2)
    while(step >=1)do
      --根据增量进行分组
      for group = 1,step do
         --对当组进行直接插入排序
         for i = group+step,#arr,step do
            local value = arr[i]
            local j = i-step
            while(j >=1 and arr[j] > value) do
               arr[j+step] = arr[j]
               j = j - step 
            end
            arr[j+step] = value   
         end   
      end
       step = math.modf(step/2)
    end  
end 

快速排序

(Quick Sort)

在待排序列中设定一个中心点pivot,将小于中心点的元素放到pivot的左边,大于中心点的元素放在Pivot的右边,然后通过递归对左右两个序列进行上述操作。

C#实现

public static void PrintList(int[] array)
{
    for (int i =0;i<array.Length; i++)
    {
        Console.Write(array[i]+"  ");    
    }
    Console.WriteLine("\n");
}

public void QuickSort(int[] arr)
{
    QSort(arr,0,arr.Length-1);
}

private void QSort(int[] arr, int low, int high)
{
    if (low < high)
    {
        int pivot = GetPivot(arr, low, high);
        QSort(arr,low,pivot-1);
        QSort(arr,pivot+1,high);
    }
}

private int GetPivot(int[] arr, int low, int high)
{
    int pivotkey = arr[low];
    while (low < high)
    {
        while (low < high && arr[high] >= pivotkey)
        {
            high--;
        }

        (arr[high], arr[low]) = (arr[low], arr[high]);
        while (low < high && arr[low] <= pivotkey)
        {
            low++;
        }
        (arr[high], arr[low]) = (arr[low], arr[high]);                          
    }

    return low;
}

int[] numList = { 10, 8, 11, 7, 4, 12, 9, 6, 5, 3 ,0,2,1,23};

Sort.QuickSort(numList);

Sort.PrintList(numList);

Lua实现

function QuickSort(arr)
   __QSort(arr,1,#arr)
end

function __QSort(arr,low,high)
   if low < high then
      local pivot = __GetPivot(arr,low,high)
      __QSort(arr,low,pivot-1)
      __QSort(arr,pivot+1,high)
   end   
end


function __GetPivot(arr,low,high)
   local pivotKey = arr[low]
   while(low<high)do
       while(low<high and arr[high] >= pivotKey)do
         high = high-1
       end  
       arr[low],arr[high] = arr[high],arr[low]
       while(low<high and arr[low] <= pivotKey) do
         low = low+1
       end  
       arr[low],arr[high] = arr[high],arr[low]
   end   

   return low
end

local numList = { 10, 8, 11, 7, 4, 12, 9, 6, 5, 3 ,0,2,1,2};
QuickSort(numList)
Print(numList)

堆排序

(Heap Sort)

将数组初始化为一个大顶堆;进行n-1趟操作,每趟操作堆顶元素与待排末尾元素交换,然后从堆顶开始将剩余元素重新调整为一个大顶堆。

C#实现

 /// <summary>
 /// 堆排序
 /// </summary>
 /// <param name="arr"></param>
 public static void HeapSort(int[] arr)
 {
     //将序列初始化为一个大顶堆
     for(int i = arr.Length / 2 - 1; i >= 0; i--)
     {
         HeapAdjust(arr, arr.Length, i);
     }

     //依次取出堆顶元素,并重新调整堆
     for (int i = arr.Length - 1; i >= 1; i--)
     {
         //将堆顶元素与当前最后一个元素交换
         (arr[0], arr[i]) = (arr[i], arr[0]);
         //重新调整堆
         HeapAdjust(arr, i, 0); 
     }
 }


 //调整序列为一个大顶堆
 public static void HeapAdjust(int[] arr,int length,int index)
 {
     int heapIndex = index;
     int leftIndex = index * 2 + 1;//左子节点索引
     int rightIndex = index * 2 + 2;//右子节点索引

     //如果左子节点大于父节点,则更新最大值
     if (leftIndex < length && arr[leftIndex] > arr[heapIndex])
     {
         heapIndex = leftIndex;  
     }
     //如果右子节点大于父节点和左子节点,则更新最大值
     if (rightIndex < length && arr[rightIndex] > arr[heapIndex])
     {
         heapIndex = rightIndex;
     }
     //如果最大值不是当前父节点,则交换父节点和最大值,并继续向下调整堆
     if (heapIndex != index)
     {
         (arr[heapIndex], arr[index]) = (arr[index], arr[heapIndex]);
         HeapAdjust(arr, length, heapIndex); 
     }
 }

Lua实现

function HeapSort(arr)
  --将序列初始化为一个大顶堆
  for i=(math.modf(#arr/2)),1,-1 do
    HeapAdjust(arr,#arr,i)
  end   

  for i = #arr,2,-1 do
    arr[1],arr[i] = arr[i],arr[1]
    HeapAdjust(arr,i-1,1)
  end   
end   

--将序列调整为大顶堆
function HeapAdjust(arr,length,index)
  local heapIndex = index
  --左子节点的索引
  local leftIndex = index*2
  --右子节点的索引
  local rightIndex = index*2+1

  --如果左子节点大于父节点,则更新最大值
  if leftIndex <= length and arr[leftIndex] > arr[heapIndex] then
    heapIndex = leftIndex
  end   

  --如果右子节点大于父节点和左子节点,则更新最大值
  if rightIndex <= length and arr[rightIndex] > arr[heapIndex] then
    heapIndex = rightIndex
  end 

  --如果最大值不是当前父节点,则交换父节点和最大值,并继续向下调整堆
  if heapIndex ~= index then
    arr[heapIndex],arr[index] = arr[index],arr[heapIndex]
    HeapAdjust(arr,length,heapIndex)
  end   
end   

归并排序

(Merge Sort)

归并排序可以分为两个主要步骤:分解和合并。在分解步骤中,序列被不断地分解成更小的子序列,直到子序列的大小为1。在合并步骤中,这些已经排序的子序列被逐步合并,直到合并成一个完整的、有序的序列。

C#实现

void MergeSort(int[] arr)
{
    int[] tempArr = new int[arr.Length];
    Merge_Sort(arr, 0, arr.Length - 1, tempArr);
}

void Merge_Sort(int[] arr,int low,int high,int[] tempArr)
{
    if (high <= low)
    {
        return;
    }

    int mid = (high - low + 1) / 2 + low;
    Merge_Sort(arr,low,mid-1,tempArr);
    Merge_Sort(arr,mid,high,tempArr);

    Merge_Arr(arr, low, mid, high, tempArr);
}

void Merge_Arr(int[] arr, int low, int mid, int high, int[] tempArr)
{
    int start = low;
    int left = low;
    int leftEnd = mid - 1;
    int right = mid;
    int tempIndex = 0;
    while (left <= leftEnd && right <= high)
    {
        if (arr[left] <= arr[right])
        {
            tempArr[tempIndex++] = arr[left++];
        }
        else
        {
            tempArr[tempIndex++] = arr[right++];
        }
    }

    while (left <= leftEnd)
    {
        tempArr[tempIndex++] = arr[left++];
    }


    for (int i = 0; i < tempIndex; i++)
    {
        arr[start++] = tempArr[i];
    }
}

Lua实现

--归并排序
function MergeSort(arr)
   local tempArr = {}
   MSort(arr,1,#arr,tempArr)
end   

function MSort(arr,low,high,tempArr)
   if low < high then
      local midIndex = math.modf((high-low+1)/2+low)
      MSort(arr,low,midIndex-1,tempArr)
      MSort(arr,midIndex,high,tempArr)
      Merge(arr,low,midIndex,high,tempArr)   
   end   
end

function Merge(arr,low,mid,high,tempArr)
   local leftStart = low
   local leftEnd = mid-1
   local rightStart = mid
   local rightEnd = high
   local tempIndex = 1
   while leftStart <= leftEnd and rightStart <= rightEnd do
      if arr[leftStart] <= arr[rightStart] then
           tempArr[tempIndex] = arr[leftStart]
           tempIndex = tempIndex + 1
           leftStart = leftStart + 1
      else   
           tempArr[tempIndex] = arr[rightStart]
           tempIndex = tempIndex + 1
           rightStart = rightStart + 1
      end
   end   

   while(leftStart <= leftEnd) do
           tempArr[tempIndex] = arr[leftStart]
           tempIndex = tempIndex + 1
           leftStart = leftStart + 1
    end  

    for i = 1,tempIndex-1 do
       arr[low] = tempArr[i]
       low = low+1
    end  
end   

折半插入排序

通过折半查找,将一个记录插入到已经排好序的有序表中,并得到一个新的、记录增1的有序表。

C#实现

public void BinaryInsertionSort(int[] array)
{
    int length = array.Length;
    for (int i = 1; i < length; i++)
    {
        int temp = array[i];
        int low = 0;
        int high = i - 1;
        while (low <= high)
        {
            int mid = (low + high) / 2;
            if (temp < array[mid])
            {
                high = mid - 1;
            }
            else
            {
                low = mid + 1;
            }
        }

        for (int j = i - 1; j >= high + 1; j--)
        {
            array[j + 1] = array[j];
        }

        array[high + 1] = temp;
    }
}

Lua实现

--二分插入排序
function BinaryInsertionSort(arr,length)
   for i=2,length do
       temp=arr[i]
       low=1
       high=i-1
      while(low<=high)do
          mid=math.floor((low+high)/2)
         if(temp<arr[mid]) then

            high=mid-1

         else
             low=mid+1

         end

      end

      for j=i-1,high+1,-1 do
         arr[j+1]=arr[j]
      end
   
      arr[high+1]=temp
   end

end

基数排序

C#实现

public override void Sort(int[] arr)
{
    //求出序列中最大的元素
    int max = arr[0];
    foreach(var item in arr)
    {
        max = item > max ? item : max;
    }

    //求出最大的位数
    int maxPlace = 1;
    while (max != 0)
    {
        max /= 10;
        maxPlace++;
    }

    //初始化10个桶,分别标志为0-9
    List<List<int>> buckets = new List<List<int>>(); 
    for(int i = 0;i < 10; i++)
    {
        buckets.Add(new List<int>());    
    }

    for(int i = 1;i <= maxPlace; i++)
    {
        int standard = (int)Math.Pow(10, i);

        //按照位数值存入桶中
        foreach (var item in arr)
        {
            int placeValue = (item % standard) / (standard / 10);
            buckets[placeValue].Add(item);   
        }

        //按顺序从桶中取出值重新放入数组中
        int index = 0;
        foreach(var bucket in buckets)
        {
            foreach(var value in bucket)
            {
                arr[index++] = value;
            }
            bucket.Clear();
        }
    }  
}

计数排序

C#实现

public override void Sort(int[] arr)
{
    //先求出序列中最大最小值
    int max = arr[0];
    int min = arr[0];
    foreach(var item in arr)
    {
        max = item > max ? item : max;
        min = item < min ? item : min;  
    }

    //初始化辅助数组的长度
    int length = max - min+1;
    var bucket = new int[length];

    foreach(var item in arr)
    {
        bucket[item-min]++;
    }

    int index = 0;
    for(int i = 0;i < bucket.Length; i++)
    {
        while (bucket[i] != 0)
        {
            arr[index++] = i + min;
            bucket[i]--;  
        }
    }
}

算法比较