ホームページ  >  記事  >  バックエンド開発  >  C#クラシックソートアルゴリズムのグラフィックコードを詳しく解説(後編)

C#クラシックソートアルゴリズムのグラフィックコードを詳しく解説(後編)

黄舟
黄舟オリジナル
2017-04-15 09:32:011478ブラウズ

この記事では主に C# について詳しく紹介します 7 つの古典的なソート アルゴリズムの第 2 部、直接挿入ソート、ヒル ソート、マージ ソートについては、一定の参考値がありますので、興味のある方は参考にしてください。最後の 3 つのソート、直接挿入ソート、ヒル ソート、マージ ソートについて説明します。

直接挿入ソート:

この種のソートは実際には非常に簡単に理解できます。非常に現実的な例は、カードのランダムなハンドをキャッチしたときに、そのサイズに応じてポーカー カードを分類する必要があります。 30 秒後、ポーカー カードが並べ替えられました。3 が 4 枚、5 枚が並べられました。そのときの並べ替え方法を思い出してください。

一番左のカードは 3、2 番目のカードは 5、3 番目のカードは再び 3、すぐに最初のカードの後ろに挿入、4 番目のカードは再び 3、とても満足です、すぐに 2 番目のカードに挿入します。裏に行き、5枚目がまた3で大砲が生まれました(笑)。

どうですか? アルゴリズムは生活のあらゆるところにあり、すでに私たちの生活と血液に組み込まれています。

以下は上の図の説明です:

この図を理解していただけたでしょうか。挿入ソートでは、配列

は「順序付き配列ブロック」と「順序なし配列」の2種類に分けられます。 「ブロック」、はい、最初のパスで、番号 20 が「順序なし配列ブロック」から順序付き配列ブロックとして抽出されます。

2 番目のパスでは、数値 60 が「順序なし配列ブロック」から抽出され、「順序付き配列ブロック」(つまり 20、60) に順序どおりに配置されます。

3 番目のパスでも同じことが当てはまりますが、違いは、10 が順序付けされた配列の値より小さいことが判明したため、20 と 60 の位置が戻されて、10 を挿入するための位置が解放されることです。

その後、すべての挿入はこのルールに従って完了できます。

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

namespace InsertSort
{
 public class Program
 {
  static void Main(string[] args)
  {
   List<int> list = new List<int>() { 3, 1, 2, 9, 7, 8, 6 };

   Console.WriteLine("排序前:" + string.Join(",", list));

   InsertSort(list);

   Console.WriteLine("排序后:" + string.Join(",", list));
  }

  static void InsertSort(List<int> list)
  {
   //无须序列
   for (int i = 1; i < list.Count; i++)
   {
    var temp = list[i];

    int j;

    //有序序列
    for (j = i - 1; j >= 0 && temp < list[j]; j--)
    {
     list[j + 1] = list[j];
    }
    list[j + 1] = temp;
   }
  }
 }
}


ヒルソート:

「挿入ソート」を観察してください: 実際、欠点があることを見つけるのは難しくありません:

データが「

5, 4, 3」の場合, 2, 1

」の場合、「順序なしブロック」にあるレコードを「順序付きブロック」に挿入すると潰れることが予想され、挿入するたびに位置が移動します。このときの効率は挿入ソートの想像ができます。

Shell はこの弱点に基づいてアルゴリズムを改善し、「縮小増分ソート方法

」と呼ばれるアイデアを組み込みました。これは実際には非常に単純ですが、注意すべき点は次のとおりです:

増分はランダムに取得されません。フォローする。

まず、増分を選択する方法を明確にする必要があります。まで: d = 1;

上の図の観察現象は:

d = 3 o 'クロック: 40 と 50 の比率。50 は 50 なので、交換されません。 30と30を比較します。30が大きいため、交換しません。

80と60を比べて、60の方が小さいので交換してください。

d=2の場合: 40と60を交換せずに比較し、60と30を交換します。このとき、交換した30は前の40より小さいため、上図のように40と30を交換する必要があります。 , 20 と 50 を比較し、交換しません。引き続き 50 と 80 を比較し、交換しません。

d=1の場合: 先ほどの挿入ソートですが、この時の並び順はほぼ順番通りなので、挿入ソートに大きな性能向上をもたらします。

「ヒルソート」は「挿入ソート」の改良版と言われているので、10,000個の数値でどれくらい高速になるかを比較する必要があります。

テストをしてみましょう:

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

namespace ShellSort
{
 public class Program
 {
  static void Main(string[] args)
  {
   //5次比较
   for (int i = 1; i <= 5; i++)
   {
    List<int> list = new List<int>();

    //插入1w个随机数到数组中
    for (int j = 0; j < 10000; j++)
    {
     Thread.Sleep(1);
     list.Add(new Random((int)DateTime.Now.Ticks).Next(10000, 1000000));
    }

    List<int> list2 = new List<int>();
    list2.AddRange(list);

    Console.WriteLine("\n第" + i + "次比较:");

    Stopwatch watch = new Stopwatch();

    watch.Start();
    InsertSort(list);
    watch.Stop();

    Console.WriteLine("\n插入排序耗费的时间:" + watch.ElapsedMilliseconds);
    Console.WriteLine("输出前十个数:" + string.Join(",", list.Take(10).ToList()));

    watch.Restart();
    ShellSort(list2);
    watch.Stop();

    Console.WriteLine("\n希尔排序耗费的时间:" + watch.ElapsedMilliseconds);
    Console.WriteLine("输出前十个数:" + string.Join(",", list2.Take(10).ToList()));

   }
  }

  ///<summary>
/// 希尔排序
///</summary>
///<param name="list"></param>
  static void ShellSort(List<int> list)
  {
   //取增量
   int step = list.Count / 2;

   while (step >= 1)
   {
    //无须序列
    for (int i = step; i < list.Count; i++)
    {
     var temp = list[i];

     int j;

     //有序序列
     for (j = i - step; j >= 0 && temp < list[j]; j = j - step)
     {
      list[j + step] = list[j];
     }
     list[j + step] = temp;
    }
    step = step / 2;
   }
  }

  ///<summary>
/// 插入排序
///</summary>
///<param name="list"></param>
  static void InsertSort(List<int> list)
  {
   //无须序列
   for (int i = 1; i < list.Count; i++)
   {
    var temp = list[i];

    int j;

    //有序序列
    for (j = i - 1; j >= 0 && temp < list[j]; j--)
    {
     list[j + 1] = list[j];
    }
    list[j + 1] = temp;
   }
  }
 }
}

スクリーンショットは次のとおりです:

wレベルのソートでは、差が70以上であることがわかります。回。

マージソート:


個人的には、分かりやすいソートは基本的にO(n^2)、理解しにくいソートは基本的にN(LogN)なので、マージソートも難しくなります。わかります、特にコードの書き方に関しては

、それを理解するのに丸一日かかりました、ふふ。

まず画像を見てください:

マージソートでは行うべきことが 2 つあります:

第一: “分”, 就是将数组尽可能的分,一直分到原子级别。

第二: “并”,将原子级别的数两两合并排序,最后产生结果。

代码:


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

namespace MergeSort
{
 class Program
 {
  static void Main(string[] args)
  {
   int[] array = { 3, 2, 1, 8, 9, 0 };

   MergeSort(array, new int[array.Length], 0, array.Length - 1);

   Console.WriteLine(string.Join(",", array));
  }

  ///<summary>
/// 数组的划分
///</summary>
///<param name="array">待排序数组</param>
///<param name="temparray">临时存放数组</param>
///<param name="left">序列段的开始位置,</param>
///<param name="right">序列段的结束位置</param>
  static void MergeSort(int[] array, int[] temparray, int left, int right)
  {
   if (left < right)
   {
    //取分割位置
    int middle = (left + right) / 2;

    //递归划分数组左序列
    MergeSort(array, temparray, left, middle);

    //递归划分数组右序列
    MergeSort(array, temparray, middle + 1, right);

    //数组合并操作
    Merge(array, temparray, left, middle + 1, right);
   }
  }

  ///<summary>
/// 数组的两两合并操作
///</summary>
///<param name="array">待排序数组</param>
///<param name="temparray">临时数组</param>
///<param name="left">第一个区间段开始位置</param>
///<param name="middle">第二个区间的开始位置</param>
///<param name="right">第二个区间段结束位置</param>
  static void Merge(int[] array, int[] temparray, int left, int middle, int right)
  {
   //左指针尾
   int leftEnd = middle - 1;

   //右指针头
   int rightStart = middle;

   //临时数组的下标
   int tempIndex = left;

   //数组合并后的length长度
   int tempLength = right - left + 1;

   //先循环两个区间段都没有结束的情况
   while ((left <= leftEnd) && (rightStart <= right))
   {
    //如果发现有序列大,则将此数放入临时数组
    if (array[left] < array[rightStart])
     temparray[tempIndex++] = array[left++];
    else
     temparray[tempIndex++] = array[rightStart++];
   }

   //判断左序列是否结束
   while (left <= leftEnd)
    temparray[tempIndex++] = array[left++];

   //判断右序列是否结束
   while (rightStart <= right)
    temparray[tempIndex++] = array[rightStart++];

   //交换数据
   for (int i = 0; i < tempLength; i++)
   {
    array[right] = temparray[right];
    right--;
   }
  }
 }
}

结果图:

ps:

插入排序的时间复杂度为:O(N^2)

希尔排序的时间复杂度为:平均为:O(N^3/2)

最坏:O(N^2)

归并排序时间复杂度为: O(NlogN)

空间复杂度为: O(N)

以上がC#クラシックソートアルゴリズムのグラフィックコードを詳しく解説(後編)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。