1. 程式人生 > >演算法導論2.4 合併排序求逆序數

演算法導論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.特點 無須增加儲存量,僅對錶的連結方式稍作改變,即可使得表處理更加