簡體   English   中英

通過遞歸反轉字符串的單詞

[英]Reverse words of string by recursion

我正在嘗試編寫一個 C function ,它接受一個字符串,並返回一個帶有反轉單詞的新字符串。 例如,輸入“How are you”應該返回“you are How”。 以下是我的試用:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *reverseWords(char *str, char *ac)
{
    if (!ac)
    {
        ac = malloc(strlen(str) + 1);
        ac[0] = '\0';
    }

    char *word = strtok(str, " ");
    if (!word)
    {
        return ac;
    }
    reverseWords(NULL, ac);
    strcat(ac, word);
    //strcat(ac, " ");
}

int main()
{
    char s[] = "How are you";
    char *reversed = reverseWords(s, NULL);
    printf("%s\n", reversed);
}

前面的代碼打印:“youareHow”,所以看起來這個想法是正確的,但只是缺少空格。 如果我試圖取消注釋 function 的最后一行,我沒有得到任何 output。 那么發生了什么? 我不明白為什么它不起作用。

您忽略了reverseWords的返回值。 我認為這沒關系,因為您參考了ac

reverseWords function 的末尾,您沒有返回包含連接字符串的ac

現在你必須取消注釋strcat(ac, " "); 這樣空間將被添加到返回值。

現在返回值將包含帶有空格的反轉字符串。

您忘記釋放分配的字符串。

當您遞歸調用reverseWords時,您會忽略返回碼,但這沒有問題,因為更高級別的調用已經引用了ac

但是最高級別的調用會將 function reverseWords留在您忘記返回值的末尾。

當您的程序似乎通過打印“youareHow”部分工作時,您實際上觀察到了未定義的行為,因為main function 顯然找到了它期望獲得返回值的預期字符串的地址。 由於 function 不返回值,這是 memory 或先前執行代碼的寄存器中剩余的東西。

像這樣更改您的代碼:

    char *word = strtok(str, " ");
    if (!word)
    {
        return ac;
    }
    reverseWords(NULL, ac);
    strcat(ac, word);
    strcat(ac, " ");
    return ac;
}

請注意,您的代碼會在每個單詞后附加一個空格。 如果將 output 更改為

    printf("<%s>\n", reversed);

要解決此問題,您可以更換

    strcat(ac, word);
    strcat(ac, " ");

    if(ac[0] != '\0')
    {
        strcat(ac, " ");
    }
    strcat(ac, word);

如果ac中的現有字符串不為空,這將在每個單詞之前插入一個空格。

我添加這個只是因為遲早我在 ideone.com 上支持的替代方法鏈接會腐爛,當它腐爛時,人們可能會想知道我在一般評論中談論的是什么。

此任務可以就地完成,無需動態分配或使用strtok 該方法使用稱為反向分區的技術,並通過執行以下操作來完成:

  1. 找到從字符串開頭到結束標記的第一個空格,該空格最初是作為終止符提供的(但每次遞歸都會減少)。 如果在該段中沒有找到空格,那么您就完成了。
  2. 將字符串開頭的段反轉為,但不包括從 (1) 中發現的中斷空格。
  3. 整個序列從開始標記反轉到結束標記。
  4. 將字符串開頭的段反轉到移動段之前的空白之前的 position。
  5. 使用 using 段進行遞歸,不包括剛剛移動的數據,也不是前導空格。

而已。 現在用數據查看它:

step 1,2. reverse the target sequence
"abc 123 xyz" ==> "cba 123 xyz"
 ^^^               ^^^

step 3. Reverse the entire sequence
"cba 123 xyz" ==> "zyx 321 abc"

step 3 Reverse the non-target sequence, not including the trailing space
"zyx 321 abc" => "123 xyz abc"
 ^^^^^^^           ^^^^^^^

對非目標段重復此操作(遞歸或迭代,沒有區別),調整結束標記以執行此操作。 第二次遍歷看起來像這樣,注意示例上方的結束標記*的 position:

        *                *
"123 xyz abc" => "321 xyz abc"
 ^^^              ^^^

        *                *
"321 xyz abc" => "zyx 123 abc"
 ^^^^^^^          ^^^^^^^

        *                *
"zyx 123 abc" => "xyz 123 abc"
 ^^^              ^^^

完成后,再次遞歸。 現在介紹看起來像這樣:

    *
"xyz 123 abc"

但請注意,字符串的開頭和結束標記之間沒有空格,所以我們完成了(如果你錯過了,我們已經完成了)。


編碼

執行此操作的實際代碼很簡單,只有指針數學需要幾眼才能完全理解。 這里是:

#include <stdio.h>
#include <string.h>
#include <ctype.h>

void reverse_letters(char *beg, char *end)
{
    while (beg < --end)
    {
        char tmp = *beg;
        *beg++ = *end;
        *end = tmp;
    }
}

char *reverse_words(char *beg, char *end)
{
    // find whitespace. if none then we're done.
    char *p = beg;
    for (; p != end && !isspace((unsigned char)*p); ++p);

    if (p != end)
    {
        reverse_letters(beg, p);
        reverse_letters(beg, end);
        reverse_letters(beg, beg + (end - p - 1));
        reverse_words(beg, beg + (end - p - 1));
    }

    return beg;
}

int main()
{
    char words[] = "abc 123 xyz";
    reverse_words(words, words + strlen(words));
    puts(words);
    return 0;
}

Output

xyz 123 abc

顯然,這是一個可以得到的尾遞歸,因此適應迭代解決方案將是微不足道的,我將其作為練習留給讀者。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM