演算法導論2.4 合併排序求逆序數
2-4 逆序對
設A[1...n]是一個包含n個不同數的陣列。如果在i<j的情況下,有A[i]>A[j],則(i,j)就稱為A中的一個逆序對。
(1)列出陣列{2,3,8,6,1}的五個逆序。
(2)如果陣列的元素取自{1,2...,n},那麼,怎樣的陣列含有最多的逆序對?
(3)插入排序的時間與輸入陣列中逆序對的數量之間有怎樣的關係?
(4)給出一個演算法,能用Θ(n㏒n)的最壞執行時間,確定n個元素的任何排列中你逆序對的數目。(提示:修改合併排序)
解答:
(1) 略
(2)逆序陣列
(3)插入排序的時間與輸入陣列中逆序對的數量呈線性正相關關係。通過觀察插入排序的演算法偽碼可知,演算法的執行步驟主要取決於內層迴圈中元素移動的次數,而每次移動就意味著陣列的逆序數減一,當排序結束時逆序數為零。
(4)依據分治法,如果我們將陣列分解成兩個子序列,分別求出兩個子序列的逆序數,再求出兩個子序列之間元素的逆序數,就可以得出整個陣列的逆序數了。可以做以下考慮:
分解:將問題分成前後兩個規模為n/2的陣列
解決:分別求解各自的逆序對數。如果子問題規模為2或1,可直接求解。
合併:此時雖然知道兩個子序列各自的逆序對數,但兩個子序列之間的逆序對數無法輕易獲知,如果進行兩兩比較的話,合併操作的時間複雜度就是n2 ,分治法沒有意義。
再考慮上述“合併”的問題,如果此時兩個子序列都是有序的話,則通過修改合併排序的MERG過程就可以得出子序列之間的逆序數:在MERG對兩個子序列的第一個元素之間進行選則時,如果前一個序列的首元素被選中,則逆序數不變——該元素不會和後一個序列中的剩下元素構成逆序對,如果第二個序列的首元素被選中,則逆序數增加“第一個序列剩下的元素數”——該元素和前一序列中剩下的每個元素構成逆序對,MERG後這些逆序對消除。按著這個思路分治演算法重新設計如下:
分解:將問題分成前後兩個規模為n/2的陣列
解決:分別進行遞迴合併排序,並記錄累加排序所消除的的逆序對數。如果子問題規模為2或1,可直接求解。
合併:通過合併排序的MERG進行合併,在MERG過程中按上述方法累加逆序數。
PS:在最初用分治法考慮問題(4)時,排序的作用在一開並不那麼明顯,但通過對“合併”的分析,要求對子問題的求解需要產生“排序”的副作用。這種”副作用“在分治法中是值得注意的。
程式碼:
import java.util.Arrays; public class Test { public static int count = 0; public static void main(String[] args) { int arr[] = { 6, 5, 4, 3, 2, 1 }; int[] result = sort_and_count(arr); System.out.println("逆序數:"+count); for(int i=0;i<result.length;i++){ System.out.print(result[i]+" "); } } private static int[] sort_and_count(int[] arr) { if (arr.length == 1) { return arr; } int length = arr.length; int alength = length / 2; int A[] = Arrays.copyOfRange(arr, 0, alength); int B[] = Arrays.copyOfRange(arr, alength, length); A = sort_and_count(A); B = sort_and_count(B); arr = merge_and_count(A, B); return arr; } //此函式有兩個功能: //(1)歸併排序中的歸併 //(2)計算逆序數 private static int[] merge_and_count(int[] a, int[] b) { int i = 0; int j = 0; int result[] = new int[a.length+b.length]; int current = 0; while (i < a.length && j < b.length) { if(a[i]<b[j]){ result[current++] = a[i]; i++; } if(a[i]>b[j]){ result[current++] = b[j]; count += (a.length - i); j++; } } if(i==a.length){ for(;j<b.length;j++){ result[current++] = b[j]; } } if(j==b.length){ for(;i<a.length;i++){ result[current++] = a[i]; } } return result; } }
相關推薦
演算法導論2.4 合併排序求逆序數
2-4 逆序對 設A[1...n]是一個包含n個不同數的陣列。如果在i<j的情況下,有A[i]>A[j],則(i,j)就稱為A中的一個逆序對。 (1)列出陣列{2,3,8,6,1}的五個逆序。 (2)如果陣列的元素取自{1,2...,n}
ACM ICPC 2011–2012, NEERC, Northern Subregional Contest J. John’s Inversions(合併排序求逆序數對數)
題目連結:http://codeforces.com/gym/100609/attachments 題目大意:有n張牌,每張牌有紅色和藍色兩面,兩面分別寫了一些數字,同種顏色的任意兩個數字若排在前面的
演算法導論--JAVA實現合併排序詳解
最近複習演算法的基本知識,主要是看《演算法導論》,根據書本中的虛擬碼寫java程式碼。以下是合併排序的程式碼: public class MergeSort { /** * @Title: merge * @Description:將左右兩個已排序的子數組合併為
歸併排序求逆序數(排序演算法)
歸併排序:歸併排序是建立在歸併操作上的一種有效的排序演算法,該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱為
排序演算法之歸併排序及利用歸併排序求逆序數
排序演算法之歸併排序 1. 自頂向下的歸併排序 中心思想 將待排序的陣列平均切分為兩半,將前半部分和後半部分分別進行排序,再講兩個有序陣列歸併到一個數組中 特點 遞迴,需要額外的空間(輔助陣列)來儲存切分完成的子陣列,主要難點在於合併
ACM_小明滾出去?(歸並排序求逆序數)
思路 name ont ans void long 輸出 lld sort 小明滾出去? Time Limit: 2000/1000ms (Java/Others) Problem Description: 老師:“小明,寫一個排序算法”; 小明: void mys
【資料結構排序】POJ1804——歸併排序求逆序數
問題描述: 給定一個數組,問最少經過多少次交換,才可以使得它有序 求解方法: 實際上就是求該陣列的逆序數,使用歸併排序即可 AC程式碼如下: #include<cstdio> #in
利用歸併排序求逆序數
假設A[1…n]是一個有n個不同元素的陣列,若i < j 且 A[i] > A[j],則對偶(i, j)稱為A的一個逆序對。例如,對於陣列[2, 3, 8, 6, 1],它的所有逆序對為(1, 5),(2, 5),(3, 4),(3, 5),(4
歸併排序求逆序數(POJ 1804,POJ 2299,HDU 4911)
首先,明確兩個概念: 逆序對:數列a[1],a[2],a[3]…中的任意兩個數a[i],a[j] (i<j),如果a[i]>a[j],那麼我們就說這兩個數構成了一個逆序對. 逆序數:一個數列中逆序對的總數. 例題一:POJ 1804. 點選開啟連結 解題思
歸併排序求逆序數(模版)
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<map> #include<cstring> #define ll
演算法導論2.1-4 考慮n位二進位制整數相加起來的問題
考慮把兩個n位二進位制整數加起來的問題,這兩個整數分別儲存在兩個n元陣列A和B中。這兩個整數的和應按二進位制形式儲存在一個(n+1)元陣列C中。使用java程式碼實現;實現程式碼如下:引數A和B為相同長度n的整形陣列,該函式返回一個長度為n+1的陣列 private s
演算法導論22.4拓撲排序 練習總結
22.4-1 給出演算法 TOPOLOGICAL-SORT 運行於圖 22-8 上時所生成的結點次序。這裡的所有假設和練習 22.3-2 一樣。 ANSWER: 22.4-2 請給出一個線性時間的演算法,演算法的輸入為一個有向無環圖 G = (V, E) 以及兩個結點
2.3合併排序的遞迴、非遞迴演算法,自然合併排序
遞迴演算法將待排元素分成大小大致相同的兩個子集合,分別對這兩個集合進行排序,最終將排好序的子集合合併。#include<iostream> #include<cstdio> void Merge(int s[],int t[],int b
【牛客網】直通bat-面試演算法精品課_第2章 排序 2.4 插入排序練習題(JAVA版)
對於一個int陣列,請編寫一個插入排序演算法,對陣列元素排序。 給定一個int陣列A及陣列的大小n,請返回排序後的陣列。 測試樣例: [1,2,3,5,2,3],6 [1,2,2,3,3,5] AC程式碼: import java.util.*; publi
【歸並排序求逆序對】
div closed spa main sed pri 歸並 逆序對 con 【AC】 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 5 co
2.4氣泡排序
氣泡排序的過程就像它的名字一樣把較小的數比作氣泡,排序的過程就是起泡不斷向上冒的過程,越小的數冒的越高。氣泡排序是從最底層的元素開始,用它和它緊挨著的上一個元素進行比較,如果下面元素小於上面元素就交換它們,否則保持原樣。然後轉移到上一個位置重複以上過程。最後,最小的元素冒到了頂部,這時我們再從最底層元素開始比
【模板】歸併排序求逆序對
歸併排序求逆序對 1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 #include <cstdio> 5 6 typedef long lon
洛谷P1908歸併排序求逆序對
#include<bits/stdc++.h> #define ll long long #define INF 0x3f3f3f3f using namespace std; int n,a[500010],c[500010]; ll ans=0; void msort(int b
高階排序演算法【2】--快速排序、歸併排序、堆排序
4、快速排序 從數列中挑出一個元素,稱為基準; 重新排列數列,所有元素比基準小的擺放在基準前面,所有元素比基準大的擺在基準後面; 在這個分割槽結束之後,該基準就位於數列的中間位置; 遞迴地對基準左右兩邊的數列進行排序。 快速排序程式碼——第一步 def qui
資料結構導論-2.4 線性表的鏈式儲存之迴圈連結串列與雙向迴圈連結串列
迴圈連結串列與雙向迴圈連結串列 一、迴圈連結串列 1.思路 對於單鏈表而言,最後一個結點的指標域是空指標,如果將該連結串列頭指標置入該指標域,則使得連結串列頭尾結點相連,就構成了單迴圈連結串列。 2.特點 無須增加儲存量,僅對錶的連結方式稍作改變,即可使得表處理更加