日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构与算法专题——第九题 外排序

發布時間:2023/12/4 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构与算法专题——第九题 外排序 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

說到排序,大家第一反應基本上是內排序,是的,算法嘛,玩的就是內存,然而內存是有限制的,總有裝不下的那一天,此時就可以來玩玩外排序,當然在我看來,外排序考驗的是一個程序員的架構能力,而不僅僅局限于排序這個層次。

一:N路歸并排序

1.概序

我們知道算法中有一種叫做分治思想,一個大問題我們可以采取分而治之,各個突破,當子問題解決了,大問題也就KO了,還有一點我們知道內排序的歸并排序是采用二路歸并的,因為分治后有LogN層,每層兩路歸并需要N的時候,最后復雜度為NlogN,那么外排序我們可以將這個 “二” 擴大到 M,也就是將一個大文件分成M個小文件,每個小文件是有序的,然后對應在內存中我們開M個優先隊列,每個隊列從對應編號的文件中讀取TopN條記錄,然后我們從M路隊列中各取一個數字進入中轉站隊列,并將該數字打上隊列編號標記,當從中轉站出來的最小數字就是我們最后要排序的數字之一,因為該數字打上了隊列編號,所以方便我們通知對應的編號隊列繼續出數字進入中轉站隊列,可以看出中轉站一直保存了M個記錄,當中轉站中的所有數字都出隊完畢,則外排序結束。如果大家有點蒙的話,我再配合一張圖,相信大家就會一目了然,這考驗的是我們的架構能力。

圖中這里有個Batch容器,這個容器我是基于性能考慮的,當batch=n時,我們定時刷新到文件中,保證內存有足夠的空間。

2.構建

1) 生成數據

這個基本沒什么好說的,采用隨機數生成n條記錄。

#region 隨機生成數據 /// <summary> /// 隨機生成數據 ///<param name="max">執行生成的數據上線</param> /// </summary> public static void CreateData(int max){var sw = new StreamWriter(Environment.CurrentDirectory + "//demo.txt");for (int i = 0; i < max; i++){Thread.Sleep(2);var rand = new Random((int)DateTime.Now.Ticks).Next(0, int.MaxValue >> 3);sw.WriteLine(rand);}sw.Close(); } #endregion

2) 切分數據

根據實際情況我們來決定到底要分成多少個小文件,并且小文件的數據必須是有序的,小文件的個數也對應這內存中有多少個優先隊列。

#region 將數據進行分份 /// <summary> /// 將數據進行分份 /// <param name="size">每頁要顯示的條數</param> /// </summary> public static int Split(int size){//文件總記錄數int totalCount = 0;//每一份文件存放 size 條 記錄List<int> small = new List<int>();var sr = new StreamReader((Environment.CurrentDirectory + "//demo.txt"));var pageSize = size;int pageCount = 0;int pageIndex = 0;while (true){var line = sr.ReadLine();if (!string.IsNullOrEmpty(line)){totalCount++;//加入小集合中small.Add(Convert.ToInt32(line));//說明已經到達指定的 size 條數了if (totalCount % pageSize == 0){pageIndex = totalCount / pageSize;small = small.OrderBy(i => i).Select(i => i).ToList();File.WriteAllLines(Environment.CurrentDirectory + "//" + pageIndex + ".txt", small.Select(i => i.ToString()));small.Clear();}}else{//說明已經讀完了,將剩余的small記錄寫入到文件中pageCount = (int)Math.Ceiling((double)totalCount / pageSize);small = small.OrderBy(i => i).Select(i => i).ToList();File.WriteAllLines(Environment.CurrentDirectory + "//" + pageCount + ".txt", small.Select(i => i.ToString()));break;}}return pageCount; } #endregion

3) 加入隊列

我們知道內存隊列存放的只是小文件的topN條記錄,當內存隊列為空時,我們需要再次從小文件中讀取下一批的TopN條數據,然后放入中轉站繼續進行比較。

#region 將數據加入指定編號隊列/// <summary>/// 將數據加入指定編號隊列/// </summary>/// <param name="i">隊列編號</param>/// <param name="skip">文件中跳過的條數</param>/// <param name="list"></param>/// <param name="top">需要每次讀取的條數</param>public static void AddQueue(int i, List<PriorityQueue<int?>> list, ref int[] skip, int top = 100){var result = File.ReadAllLines((Environment.CurrentDirectory + "//" + (i + 1) + ".txt")).Skip(skip[i]).Take(top).Select(j => Convert.ToInt32(j));//加入到集合中foreach (var item in result)list[i].Eequeue(null, item);//將個數累計到skip中,表示下次要跳過的記錄數skip[i] += result.Count();}#endregion

4) 測試

最后我們來測試一下:

  • 數據量:short.MaxValue。

  • 內存存放量:1200。

在這種場景下,我們決定每個文件放1000條,也就有33個小文件,也就有33個內存隊列,每個隊列取Top100條,Batch=500時刷新硬盤,中轉站存放332個數字(因為入中轉站時打上了隊列標記),最后內存活動最大總數為:sum=33100+500+66=896<1200。

  • 時間復雜度為N*logN。當然這個“閥值”,我們可以再仔細微調。

public static void Main(){//生成2^15數據CreateData(short.MaxValue);//每個文件存放1000條var pageSize = 1000;//達到batchCount就刷新記錄var batchCount = 0;//判斷需要開啟的隊列var pageCount = Split(pageSize);//內存限制:1500條List<PriorityQueue<int?>> list = new List<PriorityQueue<int?>>();//定義一個隊列中轉器PriorityQueue<int?> queueControl = new PriorityQueue<int?>();//定義每個隊列完成狀態bool[] complete = new bool[pageCount];//隊列讀取文件時應該跳過的記錄數int[] skip = new int[pageCount];//是否所有都完成了int allcomplete = 0;//定義 10 個隊列for (int i = 0; i < pageCount; i++){list.Add(new PriorityQueue<int?>());//i:記錄當前的隊列編碼//list: 隊列數據//skip:跳過的條數AddQueue(i, list, ref skip);}//初始化操作,從每個隊列中取出一條記錄,并且在入隊的過程中//記錄該數據所屬的 “隊列編號”for (int i = 0; i < list.Count; i++){var temp = list[i].Dequeue();//i:隊列編碼,level:要排序的數據queueControl.Eequeue(i, temp.level);}//默認500條寫入一次文件List<int> batch = new List<int>();//記錄下次應該從哪一個隊列中提取數據int nextIndex = 0;while (queueControl.Count() > 0){//從中轉器中提取數據var single = queueControl.Dequeue();//記錄下一個隊列總應該出隊的數據nextIndex = single.t.Value;var nextData = list[nextIndex].Dequeue();//如果改對內彈出為null,則說明該隊列已經,需要從nextIndex文件中讀取數據if (nextData == null){//如果該隊列沒有全部讀取完畢if (!complete[nextIndex]){AddQueue(nextIndex, list, ref skip);//如果從文件中讀取還是沒有,則說明改文件中已經沒有數據了if (list[nextIndex].Count() == 0){complete[nextIndex] = true;allcomplete++;}else{nextData = list[nextIndex].Dequeue();}}}//如果彈出的數不為空,則將該數入中轉站if (nextData != null){//將要出隊的數據 轉入 中轉站queueControl.Eequeue(nextIndex, nextData.level);}batch.Add(single.level);//如果batch=500,或者所有的文件都已經讀取完畢,此時我們要批量刷入數據if (batch.Count == batchCount || allcomplete == pageCount){var sw = new StreamWriter(Environment.CurrentDirectory + "//result.txt", true);foreach (var item in batch){sw.WriteLine(item);}sw.Close();batch.Clear();}}Console.WriteLine("恭喜,外排序完畢!");Console.Read();}

總結

以上是生活随笔為你收集整理的数据结构与算法专题——第九题 外排序的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。