演算法7.自然合併排序與最長公共子序列。
- 如果陣列中部分元素已按自然數順序排放,例如,陣列
,則初期自然排好序的子陣列段顯然有4段,分別為
,
,
和
。請充分利用上述特點設計並實現一個自然合併排序演算法。
(1) 演算法設計思路
先對陣列進行一次線性掃描,記錄下部分有序陣列的斷開位置和個數,個數用於判斷最後一個斷開的位置是否為陣列末尾,位置用於合併陣列。迴圈-依次將相鄰的兩兩陣列進行合併,直到最後剩下一個陣列,即為合併排序好的陣列。
(2) 演算法實現的虛擬碼
功能描述:對陣列進行線性掃描,記錄下該斷點的位置和斷點個數
輸入:陣列大小n
輸出:陣列劃分後的斷點個數+1
int fenduan(int n)
int j=0;
rem[j++]=0
for i=0 to n-2
if (arr[i]>arr[i+1]) rem[j]=i;j++;//記錄斷點的位置
end for
rem[j++]=n-1;
return j;
功能描述:通過記錄的斷點位置將兩兩相鄰的偽陣列進行合併排序
輸入:起始位置a,斷點位置m,終點位置r
輸出:
void merge(int a,int m,int r)
int i=a,j=m+1,k=a;
while (i<=m&&j<=r)
if (arr[i]<=arr[j]) newarr[k++]=arr[i++]
else newarr[k++]=arr[j++];
end while
if (i>m)
for (int q=j to r) newarr[k++]=arr[q] end for
else for (int q=I to m) newarr[k++]=arr[q] end for
功能描述:迴圈活動最終的自然合併排序
輸入:陣列的大小n
輸出:
sort(int num)
int a=huafen(num)
while (a!=2 )
for (int i=0 to a-1)
if (i==0)
merge(rem[i] ,rem[i+1], rem[i+2]);
else if (i+2>a-1)
merge(rem[i+1],rem[i+1],rem[i+1])
else merge(rem[i]+1,rem[i+1],rem[i+2])
end for
/將合併好的陣列複製到原陣列
for (int j=0;j<n;j++)
arr[j]=newarr[j];end for
a=huafen(n);//繼續獲得斷點個數+1
(3) 實現程式碼
import java.util.Scanner;
public class main {
static int[] arr;//建立錄入資料用的陣列
static int[] newarr;//用於儲存合併後的陣列
static int[] rem;//記錄陣列斷點的位置
static int n;//陣列的大小
//自然合併主方法
static void sort(int num){
int a=huafen(num);//斷點的個數+1
while (a!=2){//如果只有一個斷點,則證明已經排好順序
int i=0;
for (i=0;i<a-1;i+=2){
if (i==0)//對第一隊有序陣列進行排序
merge(rem[i] ,rem[i+1], rem[i+2]);
else if (i+2>a-1)//剩餘的部分有序陣列無法兩兩匹配
merge(rem[i+1],rem[i+1] , rem[i+1]);
else
merge(rem[i]+1, rem[i+1], rem[i+2]);
// else if (i+2>a-1&&i+1==a-1)//最後兩個有序陣列兩兩合併
// merge(rem[i], rem[i], rem[i+1]);
// else//有序陣列兩兩合併
// merge(rem[i] ,rem[i+1], rem[i+2]);
}
//將合併好的陣列複製到原陣列
for (int j=0;j<n;j++)
arr[j]=newarr[j];
a=huafen(n);//繼續獲得斷點個數+1
}
}
//劃分陣列的有序部分,並且記錄下該斷點的位置和斷點個數
private static int huafen(int n) {
int num=0;
rem[num++]=0;
for (int i=0;i<n-1;i++){
if (arr[i]>arr[i+1])
rem[num++]=i;
}
rem[num++]=n-1;//將最後一個斷點位置賦值為陣列最後一個位置,便於排序
return num;
}
//進行合併排序
private static void merge(int a, int m, int r) {
int i=a;
int j=m+1;
int k=a;
while (i<=m&&j<=r){
if (arr[i]<=arr[j])
newarr[k++]=arr[i++];
else {
newarr[k++]=arr[j++];
}
}
if (i>m)
for (int q=j;q<=r;q++)
newarr[k++]=arr[q];
else {
for (int q=i;q<=m;q++)
newarr[k++]=arr[q];
}
}
public static void main(String []args){
Scanner in=new Scanner(System.in);
System.out.println("輸入陣列的大小");
n=in.nextInt();
//初始化資料
newarr = new int[n];
arr=new int[n];
rem=new int[n+1];
System.out.println("錄入資料,資料用空格格開");
String line=in.nextLine();
line=in.nextLine();
String arrg[]=line.split(" ");
for (int j=0;j<arrg.length;j++)
arr[j]=Integer.parseInt(arrg[j]);
sort(n);
System.out.println("自然合併排序後");
for (int i=0;i<n;i++)
System.out.print(newarr[i]+" ");
}
}
(4) 演算法執行結果及計算時間複雜度分析
時間複雜度為O(n)
(5) 體會
自然合併排序演算法比一般合併排序演算法多了一個掃描的方法。通過掃描後記錄斷點的位置和斷點的個數,可以很清晰的使用合併演算法來合併兩個相鄰的有序陣列斷。在每次的合併後,繼續掃描一次,然後再合併,直到掃描到只有一個斷點個數。在編寫程式碼的過程中,老是在迴圈遞迴合併演算法卡住了,不能很好的判斷要合併的相鄰的兩個陣列斷的起點,斷開的位置,和終點位置,導致耗費了比較多的時間
- 最長公共子序列LCS問題:給定2個序列X和Y,當另一序列Z既是X的子序列又是Y的子序列時,稱Z是序列X和Y的公共子序列。給定X = {x1, x2, …, xm}和Y = {y1, y2, …, yn},請找出X和Y的最長公共子序列。例如:
輸入:X = ABCHFGASJKXBD Y = DBDHASXB
輸出:LCS = BHASXB
(1) 演算法設計思路
建立二維陣列c儲存X和Y的最長公共子序列的長度,二維陣列b記錄c的值是由哪一個子問題解得到的,然後構造最長公共子序列。
(2) 演算法實現的虛擬碼
功能描述:建立備忘錄,記錄最長子序列的長度和對應所在位置
輸入:字串x,y和其長度n,m。備忘錄c,b
輸出:備忘錄c,b
void max(int n,int m ,char[] x,char[] y,int[][] c,int[][] b)
for(int I to n) c[i][0]=0
for (int j to m) c[0][j]=0;
for (int i to n)
for (int j to m)
if (x[i]=y[j]) c[i][j]=c[i-1][j-1]+1;b[i][j]=1;
else if (c[i-1][j]>=c[i][j-1]) c[i][j]=c[i-1][j];b[i][j]=2;
else c[i][j]=c[i][j-1];b[i][j]=3;
end for
end for
功能描述:使用備忘錄輸出共同的子序列
輸入:字串x和x的長度,y的長度。備忘錄b
輸出:最長公共子序列
if (i==0||j==0)return;
if (b[i][j]==1) input(i-1, j-1,x,b); 輸出(x[i]);
else if (b[i][j]==2)input(i-1, j,x,b);
else input(i, j-1,x,b);
(3) 實現程式碼
package 演算法分析;
import java.util.Scanner;
public class main {
//建立備忘錄,記錄所有的資料
static void max(int n,int m,char[] x,char[] y,int[][] c,int[][] b){
//初始化陣列
for (int i=1;i<=n;i++)
c[i][0]=0;
for (int j=1;j<=m;j++)
c[0][j]=0;
/*
* c[i][j]=① 0 ,i=0,j=0
* ②c[i]-1[j-1]+1 , Xi=Yj
* ③max{ (c[i][j-1],c[i-1][j]) } , Xi!=Yi
*/
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++){
if (x[i]==y[j]){
c[i][j]=c[i-1][j-1]+1;
b[i][j]=1;
}
else if (c[i-1][j]>=c[i][j-1]){
c[i][j]=c[i-1][j];
b[i][j]=2;
}
else {
c[i][j]=c[i][j-1];
b[i][j]=3;
}
}
}
//使用備忘錄
static void input(int i,int j,char[] x,int[][] b){
if (i==0||j==0)
return;
if (b[i][j]==1){//如果Xi=Yj,遞迴,輸出該值
input(i-1, j-1,x,b);
System.out.print(x[i]);
}
else if (b[i][j]==2){
input(i-1, j,x,b);
}
else
input(i, j-1,x,b);
}
public static void main(String []args){
Scanner in=new Scanner(System.in);
System.out.println("輸入字串x[]");
String line=in.nextLine();
char[] x;
x=line.toCharArray();
int n=x.length;//獲取字串x的長度
//為x頭加上空格
char c1[]=null;
c1=x;
x=new char[n+1];
x[0]=' ';
for (int i=1;i<=n;i++)
x[i]=c1[i-1];
System.out.println("輸入字串Y[]");
String line2=in.nextLine();
char[] y;
y=line2.toCharArray();
int m=y.length;//獲取字串y的長度
//為字串y頭加上空格
char c2[]=null;
c2=y;
y=new char[m+1];
y[0]=' ';
for (int i=1;i<=m;i++)
y[i]=c2[i-1];
int[][] c=new int[n+1][m+1];
int[][] b=new int[n+1][m+1];
max(n, m, x, y, c, b);
input(n, m, x, b);
}
}
(4) 演算法執行結果及計算時間複雜度分析
O(mn)+O(n+m)
(5) 體會
重在理解。備忘錄的使用非常的巧妙,經常會忘記使用備忘錄而導致結果錯誤。使用動態規劃的時候,先找出最優解的性質,並刻畫其結構特徵,然後遞迴定義最優解,再自底而上的方式計算最優值,最後根據計算最優值時得到的資訊,構造最優解。分析最優解的性質很重要,也比較有難度。通過做這道題,發現我現在對動態規劃的理解還差得遠呢,但是隻要肯努力,就沒有什麼不可能。
相關推薦
演算法7.自然合併排序與最長公共子序列。
如果陣列中部分元素已按自然數順序排放,例如,陣列 ,則初期自然排好序的子陣列段顯然有4段,分別為 , , 和 。請充分利用上述特點設計並實現一個自然合併排序演算法。 (1) 演算法設計思路 先對陣列進行一次線性掃描,
最長公共子串與最長公共子序列
兩個 ring 數組存儲 src str int sdf range div 一、最長公共子串(Longest Common Substring) 遍歷的時候用一個二維數組存儲相應位置的信息,如果兩個子串1與子串2相應位置相等:則看各自前一個位置是否相等,相等則該位置值B[
資料結構演算法題/兩個字串的最長公共子序列
一,問題描述 給定兩個字串,求解這兩個字串的最長公共子序列(Longest Common Sequence)。比如字串1:BDCABA;字串2:ABCBDAB 則這兩個字串的最長公共子序列長度為4,最長公共子序列是:BCBA 二,演算法求解 這是一個動態規劃的題目。
最長公共子串與最長公共子序列(動歸實現)
什麼是子序列?一個給定的序列的子序列,就是將給定序列中零個或多個元素去掉之後得到的結果。 什麼是子串?給定串中任意個連續的字元組成的子序列稱為該串的子串。(相對於子序列,子串是連續的) 如abcde
演算法學習——動態規劃 例題:最長公共子序列問題(java)
題目: 給定兩個字串str1和str2,返回兩個字串的最長公共子序列.例如,str1="1A2C3D4B56",str2="B1D23CA45B6A","123456"或者"12C4B6' 動態規劃思想: 先用一個比,左邊加一個字元右面加一個字元依次比較dp[i][j] dp[i][j]意思
編輯距離與最長公共子序列總結
前言: 其實編輯距離和最長公共子序列是對同一個問題的描述,都能夠顯示出兩個字串之間的“相似度”,即它們的雷同程度。而子序列與字串的區別在於字串是連續的,子序列可以不連續,只要下標以此遞增就行。 編輯距離: Problem description: 設A 和B 是2 個
問題描述:求兩個字串str1、str2的最長公共子序列。
首先明白兩個區別: 1、最長公共子串:子串是串的一個連續的部分,在原字串中位置是連續的 2、最長公共子序:不改變序列的順序,從序列中去掉任意的元素而獲得新的序列,也就是說子序在原字串中位置可以不連續。 遞推方程: 步驟:1序列str1和序列str2 ·長度分別為m和
常考的經典演算法--最長公共子序列(LCS)與最長公共子串(DP)
https://blog.csdn.net/qq_31881469/article/details/77892324 《1》最長公共子序列(LCS)與最長公共子串(DP) http://blog.csdn.net/u012102306/article/details/53184446 h
演算法設計與分析學習筆記——最長公共子序列
最長公共子問題待解決問題: 給定兩個序列X和Y,求其一個最長公共的序列Z。 補充解釋:X(m)={x1, x2,,,,,xm},Y(n)={y1, y2,,,,,yn},X和Y可以有共同的元素,Z是這些共同元素的集合,其元素順序在XYZ中都是升序排序的(Z中元素的
【資料結構與演算法】最長公共子串 最長公共子序列
1.最長公共子串:找出s和t的公共子字串的最大長度。 使用dp,定義子問題dp[i][j]:公共子串結束在位置i,j的長度。如果s[i] != t[j],那麼很顯然是0,否則dp[i][j] = dp[i - 1][j - 1] + 1。 程式碼: public int
011-最長公共子序列-動態規劃-《演算法設計技巧與分析》M.H.A學習筆記
給出兩個長度分別為n和m的字串A和B,確定A和B中最長公共子序列的長度。 樸素演算法:列舉A中所有的子序列2n個,並逐個判斷其是否在B中(Θ(m)耗費)。時間複雜度為Θ(m2n)。 利用動態規劃可
最長公共子序列-LCS問題 (LCS與LIS在特殊條件下的轉換) [洛谷1439]
一個 har define 分享 amp lis read ios stack 題目描述 給出1-n的兩個排列P1和P2,求它們的最長公共子序列。 輸入 第一行是一個數n, 接下來兩行,每行為n個數,為自然數1-n的一個排列。 輸出 一個數,即最長公
【演算法 in python | DP】LCS最長公共子串
1. LCS,最長公共子串 動態規劃,狀態轉移方程: #該版本是返回最長公共子串和其長度,若只返回長度,則可以簡化 def lcs(s1, s2): l1 = len(s1) l2 = len(s2) # res[i][j]儲存子串s1[0:i] 和 子串s2[
演算法 求一個數組的最長遞減子序列 C
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
演算法 | 最長公共子序列
#include<stdio.h> #include<string.h> #define MaxN 10000 #define MaxC 10000 int Val[MaxN][MaxN]; double binaryKn
LCS 演算法 最長公共子序列
最長公共子序列不需要在原序列中佔用連續的位置 #include <iostream> #include <string> #include <cstring> #include <vector> #include <algorithm&g
演算法--最長公共子序列以及子串
問題1:給定兩個字串,求其最大公共子序列 例如asbcdfg和scdfgjkl, 則返回scdfg 使用動態規劃求解, 假設s1=<x1,x2....xn> s2=<y1,y2..ym> 令f[i][j]表示串s1以索引i結尾,串s2以索引j結尾的
哈爾濱理工大學軟體與微電子學院第八屆程式設計競賽同步賽(高年級) E 小樂樂匹配字串 【最長公共子序列】
傳送門:https://ac.nowcoder.com/acm/contest/301/E 求最長公共子序列。 立個 flag 搞dp。 AC code: #include <cstdio> #include <iostream> #inc
最大子段和與最長遞增子序列(貪心與動態規劃)
話不多說先上程式碼。。。。。 最大子段和 題目描述 給出一段序列,選出其中連續且非空的一段使得這段和最大。 輸入輸出格式 輸入格式: 第一行是一個正整數NNN,表示了序列的長度。 第二行包含NNN個絕對值不大於100001000010000的
[Java] 藍橋杯ADV-202 演算法提高 最長公共子序列
問題描述給定兩個字串,尋找這兩個字串之間的最長公共子序列。輸入格式輸入兩行,分別包含一個字串,僅含有小寫字母。輸出格式最長公共子序列的長度。樣例輸入abcdgh aedfhb樣例輸出3樣例說明最長公共子