1. 程式人生 > >演算法7.自然合併排序與最長公共子序列。

演算法7.自然合併排序與最長公共子序列。

  1. 如果陣列中部分元素已按自然數順序排放,例如,陣列
    這裡寫圖片描述

,則初期自然排好序的子陣列段顯然有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) 體會
自然合併排序演算法比一般合併排序演算法多了一個掃描的方法。通過掃描後記錄斷點的位置和斷點的個數,可以很清晰的使用合併演算法來合併兩個相鄰的有序陣列斷。在每次的合併後,繼續掃描一次,然後再合併,直到掃描到只有一個斷點個數。在編寫程式碼的過程中,老是在迴圈遞迴合併演算法卡住了,不能很好的判斷要合併的相鄰的兩個陣列斷的起點,斷開的位置,和終點位置,導致耗費了比較多的時間

  1. 最長公共子序列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問題 (LCSLIS在特殊條件下的轉換) [洛谷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樣例說明最長公共子