1. 程式人生 > >劍指offer-28.全排列

劍指offer-28.全排列

回溯 continue 重復 aux RM true 輸出 log 第一個

0 題目

輸入一個字符串,按字典序打印出該字符串中字符的所有排列。例如輸入字符串abc,則打印出由字符a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab和cba。

1 分析

這屬於全排列算法。

假設,我們先選定第i個,為某個排序結果的第一個。那麽只要將第i個元素,和第一個元素交換。然後繼續全排列剩下的元素就行了。

因此有

vector<string> Permutation(string str)
{
    vector<string> ret;
    if (str.empty())
    {
        return ret;
    }

    // 相等的情況可以直接跳過
    // 所以做了排序
    sort(str.begin(), str.end());
    string tmp;
    aux(str, 0, tmp, ret);

    // 這裏排序,因為和牛客的輸出順序不匹配所以排序
    sort(ret.begin(), ret.end());

    return ret;
}

void aux(string &str, int index, string &tmp, vector<string> &ret)
{
    if (index == str.size())
    {
        // 當 要開始全排列的index已經是 size() 了,那麽表示,就不需要重拍了
        // 因此壓入結果,返回
        ret.push_back(str);
        return;
    }

    for (int i = index; i < str.size(); ++i)
    {
        if (i != index && str[i] == str[index])
        {
            // 跳過重復的,重復的就不做了
            continue;
        }

        // 將選定的第一個交換過去,然後遞歸的全排列剩下的
        swap(str[index], str[i]);
        aux(str, index + 1, tmp, ret);

        // 這裏在交換回去,也就是說,當以第i個為結果的第一個元素,遍歷完以後
        // 以第i+1個為第一個元素遍歷開始的時候,str與傳入時候的順序一樣
        swap(str[index], str[i]);
    }
}

  

如果不采用這種交換的方式,可以采用memo的方式記錄某個點是否加入,然後遍歷完仍然需要回溯。

但是這樣的化,在for中,就需要多次遍歷一遍memo中的元素,因此,效率低。

劍指offer-28.全排列