出棧順序 與 卡特蘭數(Catalan)的關系


一,問題描述

給定一個以字符串形式表示的入棧序列,請求出一共有多少種可能的出棧順序?如何輸出所有可能的出棧序列?

比如入棧序列為:1 2 3  ,則出棧序列一共有五種,分別如下:1 2 3、1 3 2、2 1 3、2 3 1、3 2 1

 

二,問題分析

先介紹幾個規律:

對於出棧序列中的每一個數字,在它后面的、比它小的所有數字,一定是按遞減順序排列的。

比如入棧順序為:1 2 3 4。

出棧順序:4 3 2 1是合法的,對於數字 4 而言,比它小的后面的數字是:3 2 1,且這個順序是遞減順序。同樣地,對於數字 3 而言,比它小的后面的數字是: 2 1,且這個順序是遞減的。....

出棧順序:1 2 3 4 也是合法的,對於數字 1 而言,它后面沒有比它更小的數字。同樣地,對於數字 2 而言,它后面也沒有比它更小的數字。

出棧順序:3 2 4 1 也是合法的,對於數字 3 而言,它后面比 3 小的數字有: 2 1,這個順序是遞減的;對於數字 2 而言,它后面的比它 小的數字只有 1,也算符合遞減順序;對於數字 4 而言,它后面的比它小的數字也只有1,因此也符合遞減順序。

出棧順序:3 1 4 2 是不合法的,因為對於數字 3 而言,在3后面的比3小的數字有:1 2,這個順序是一個遞增的順序(1-->2)。

 

因此,當給定一個序列時,通過這個規律 可以輕松地判斷 哪些序列是合法的,哪些序列是非法的。

 

②給定一個入棧順序:1  2  3 .... n,一共有多少種合法的出棧順序?參考:百度百科卡特蘭數

答案是 卡特蘭數。即一共有:h(n)=c(2n,n)/(n+1) 種合法的出棧順序。

如果僅僅只需要求出一共有多少種合法的出棧順序,其實就是求出組合 C(2n,n)就可以了。而求解C(2n,n),則可以用動態規划來求解,具體可參考: 排列與組合的一些定理

 

三,代碼實現

給定一個入棧順序,比如 1 2 3 ,如何輸出所有可能的出棧順序?

思路①:先求出入棧順序的所有排列(即全排列),並將排列保存到一個LinkedList<String>中,然后依次遍歷每一個序列,判斷該序列是否是合法的序列。

所謂合法的序列,就是滿足上面的規律1:對於出棧序列中的每一個數字,在它后面的、比它小的所有數字,一定是按遞減順序排列的。 關於如何求解一個序列的全排列,可參考:JAVA求解全排列 

完整代碼實現如下:(實現得不好,感覺比較復雜)

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;

public class AllStackPopOrder {
        
    
    public static LinkedList<String> allPermutation(String str){
        if(str == null || str.length() == 0)
            return null;
        //保存所有的全排列
        LinkedList<String> listStr = new LinkedList<String>();
        
        allPermutation(str.toCharArray(), listStr, 0);
        
        //print(listStr);//打印全排列
        return listStr;
    }
    
    
    private static void allPermutation(char[] c, LinkedList<String> listStr, int start){

        if(start == c.length-1)
            listStr.add(String.valueOf(c));
        else{
            for(int i = start; i <= c.length-1; i++)
            {
                //只有當沒有重疊的字符 才交換
                if(!isSwap(c, start, i))
                {
                    swap(c, i, start);//相當於: 固定第 i 個字符
                    allPermutation(c, listStr, start+1);//求出這種情形下的所有排列
                    swap(c, start, i);//復位
                }
            }
        }
    }
    
    private static void swap(char[] c, int i, int j){
        char tmp;
        tmp = c[i];
        c[i] = c[j];
        c[j] = tmp;
    }
    
    private static void print(LinkedList<String> listStr)
    {
        Collections.sort(listStr);//使字符串按照'字典順序'輸出
        for (String str : listStr) {
            System.out.println(str);
        }
        System.out.println("size:" + listStr.size());
    }
    
    //[start,end) 中是否有與 c[end] 相同的字符
    private static boolean isSwap(char[] c, int start, int end)
    {
        for(int i = start; i < end; i++)
        {
            if(c[i] == c[end])
                return true;
        }
        return false;
    }
    
    public static LinkedList<String> legalSequence(LinkedList<String> listStr){
        Iterator<String> it = listStr.iterator();
        String currentStr;
        while(it.hasNext())//檢查全排列中的每個序列
        {
            currentStr = it.next();
            if(!check(currentStr))
                it.remove();//刪除不符合的出棧規律的序列
        }
        return listStr;
    }
    //檢查出棧序列 str 是否 是合法的出棧 序列
    private static boolean check(String str){
        boolean result = true;
        char[] c = str.toCharArray();
        char first;//當前數字.
        int k = 0;//記錄 compare 數組中的元素個數
        char[] compare = new char[str.length()];
        for(int i = 0; i < c.length; i++)
        {
            first = c[i];
            //找出在 first 之后的,並且比 first 小的數字
            for(int j = i+1; j < c.length; j++)
            {
                if(c[j] > first)
                    continue;
                else
                {
                    compare[k++] = c[j];//將比當前數字小的 所有數字 放在compare數組中
                }
            }
            if(k == 0)
                continue;
            else{
                for(int m = 0; m < k-1; m++)//判斷 compare 數組是否是 遞減的順序
                {
                    if(compare[m] < compare[m+1])
                    {
                        result = false;//不符合遞減順序
                        return result;
                    }
                }
            }
            k=0;
        }
        return result;
    }
    
    //hapjin test
    public static void main(String[] args) {
        String str = "1234";
        LinkedList<String> listStr = legalSequence(allPermutation(str));
        print(listStr);
    }
}
View Code

 

思路②:直接求出合法的出棧序列。【而不是像思路①那樣:先求出所有可能的出棧序列(求全排列),然后再找出合法的出棧序列。】

待完成。

 

四,參考資料

JAVA求解全排列

出棧順序(卡特蘭數)

可能的出棧順序


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM