數據結構(十一)——遞歸

數據結構(十一)——遞歸

1、遞歸簡介

一、遞歸簡介

遞歸是一種數學上分而自治的思想。
    A、將原問題分解爲規模較小的問題進行處理
        分解後的問題與原問題類型徹底相同,當規模較小。
        經過小規模問題的解,可以輕易求得原生問題的解
    B、問題的分解時有限的
        當邊界條件不能知足時,分解問題(繼續遞歸)
        當邊界條件知足時,直接求解(遞歸結束)

二、遞歸模型

遞歸模型的通常表示法:

數據結構(十一)——遞歸

2、遞歸的應用

遞歸在程序設計中的應用
    遞歸函數:
        函數體中存在自我調用的函數
        遞歸函數必須有遞歸出口(邊界條件)
        函數的無限遞歸將致使程序崩潰
        使用遞歸函數時不要陷入遞歸函數的執行細節,應首先創建遞歸模型和確立邊界條件。

一、求和的遞歸實現

數據結構(十一)——遞歸

int sum(unsigned int n)
{
  int ret;
  if(n > 1)
  {
      ret = n + sum(n - 1);
  }
  else if(n == 1)
  {
      ret =  1;
  }
  return ret;
}

二、斐波那契數列的實現

數據結構(十一)——遞歸

unsigned int Fibonacci(unsigned int n)
{
  unsigned int ret ;
  if(2 < n)
  {
      ret = Fibonacci(n -1) + Fibonacci(n -2);
  }
  else if((n == 1) || (n == 2))
  {
      ret = 1;
  }
  return ret;
}

三、字符串長度的遞歸實現

數據結構(十一)——遞歸

int _strlen_(const char* s)
{
  int ret = 0;
  if(*s != '\0')
  {
      ret = 1 + _strlen_(s+1);
  }
  else
  {
      ret = 0;
  }
  return ret;
}
代碼簡化:
unsigned int _strlen_(const char* s)
{
  return s?((*s)?(1 + _strlen_(s + 1)):0):0;
}

四、單鏈表翻轉的遞歸實現

數據結構(十一)——遞歸

typedef struct
{
  int data;
  Node* next;
}Node;

Node* reverse(Node* list)
{
  Node* ret = NULL;
  if(list == NULL || list->next == NULL)
  {
    ret = list;
  }
  else
  {
    Node* guard = list->next;
    ret = reverse(list->next);
    guard->next = list;
    list->next = NULL;
  }
  return ret;
}

五、單向排序鏈表的合併

數據結構(十一)——遞歸

typedef struct
{
  int data;
  Node* next;
}Node;
Node* merge(Node* list1, Node* list2)
{
  Node* ret = NULL;
  if(NULL == list1)
  {
    ret = list2;
  }
  else if(NULL == list2)
  {
    ret = list1;
  }
  else if(list1->data < list2->data)
  {
    list1->next = merge(list1->next,list2);
    ret = list1;
  }
  else
  {
    list2->next = merge(list2->next, list1);
    ret = list2;
  }
  return ret;
}

六、漢諾塔問題求解

漢諾塔問題:
        A、將木塊藉助B柱由A柱移動到C柱
        B、每次只能移動一塊木塊
        C、小木塊只能出如今大木塊之上

數據結構(十一)——遞歸
漢諾塔問題的解決方案:
A、將n-1個木塊藉助C柱由A柱移動到B柱
B、將最底層的木塊直接移動到C柱
C、將n-1個木塊藉助A柱由B柱移動到C柱
數據結構(十一)——遞歸算法

/*********************************
 * n:木塊的數量
 * A:A柱
 * B:B柱
 * C:C柱
 * 漢諾塔問題:將n個木塊從A柱藉助B柱移動到C柱
 * ******************************/
void HanoiTower(int n, char A, char B, char C)
{
  if(n == 1)
  {
      cout << A << "-->" << C << endl;
  }
  else
  {
      //將A柱上的n-1個木塊藉助C柱移動到B柱
      HanoiTower(n-1, A, C, B);
      //將A柱上的木塊,直接移動到C柱
      HanoiTower(1, A, B, C);
      //將B柱上的n-1個木塊藉助A柱移動到C柱
      HanoiTower(n-1, B, A, C);
    }
}

七、全排列問題的遞歸求解

數據結構(十一)——遞歸

void permutation(char* s, char* ret)
{
  if('\0' == *s)
  {
      cout << ret << endl;
  }
  else
  {
    int len = strlen(s);
    for(int i = 0; i < len; i++)
    {
      if(0 == i || (s[0] != s[i]))
      {
        swap(s[0], s[i]);
        permutation(s+1, ret);
        swap(s[0], s[i]);
      }
    }
  }
}

3、函數調用與遞歸思想

一、函數的調用過程

程序運行後有一個特殊的內存區域(棧)供函數調用使用:
        A、用於保存函數中的實參、局部變量、臨時變量等
        B、從起始地址開始往一個方向增加
        C、有一個專用指針標識當前已用內存的頂部
    程序中的棧區:

數據結構(十一)——遞歸
逆序打印單鏈表中的偶數結點:
數據結構(十一)——遞歸數組

typedef struct
{
  int data;
  Node* next;
}Node;
void r_print_even(Node* list)
{
  if(NULL !=list)
  {
      r_print_even(list->next);
      if(list->data %2 == 0)
      {
        cout << list->data << endl;
      }
  }
}

二、回溯算法

回溯算法實際上一個相似枚舉的搜索嘗試過程,主要是在搜索嘗試過程當中尋找問題的解,當發現已不知足求解條件時,就「回溯」返回,嘗試別的路徑。回溯法是一種選優搜索法,按選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步從新選擇,這種走不通就退回再走的技術爲回溯法,而知足回溯條件的某個狀態的點稱爲「回溯點」。數據結構

回溯算法的基本思想是:從一條路往前走,能進則進,不能進則退回來,換一條路再試。八皇后問題就是回溯算法的典型,第一步按照順序放一個皇后,而後第二步符合要求放第2個皇后,若是沒有位置符合要求,那麼就要改變第一個皇后的位置,從新放第2個皇后的位置,直到找到符合條件的位置就能夠了。回溯在迷宮搜索中使用很常見,就是這條路走不通,而後返回前一個路口,繼續下一條路。回溯算法說白了就是窮舉法。不過回溯算法使用剪枝函數,剪去一些不可能到達最終狀態(即答案狀態)的節點,從而減小狀態空間樹節點的生成。回溯法是一個既帶有系統性又帶有跳躍性的的搜索算法。它在包含問題的全部解的解空間樹中,按照深度優先的策略,從根結點出發搜索解空間樹。算法搜索至解空間樹的任一結點時,老是先判斷該結點是否確定不包含問題的解。若是確定不包含,則跳過對以該結點爲根的子樹的系統搜索,逐層向其祖先結點回溯。不然,進入該子樹,繼續按深度優先的策略進行搜索。回溯法在用來求問題的全部解時,要回溯到根,且根結點的全部子樹都已被搜索遍才結束。而回溯法在用來求問題的任一解時,只要搜索到問題的一個解就能夠結束。這種以深度優先的方式系統地搜索問題的解的算法稱爲回溯法,它適用於解一些組合數較大的問題。`ide

三、八皇后問題

在8X8的國際象棋棋盤上,有8個皇后,每一個皇后佔一個格子,要求皇后之間不會出現相互***的現象(任意兩個皇后不能處在同一行、同一列或者同一對角線上)。
數據結構(十一)——遞歸
棋盤的定義:
二維數組(10X10),0表示位置爲空,1表示皇后,2表示邊界。
位置的定義:函數

struct Pos
{
  int x;
  int y;
};
方向定義:
        水平左方向(-1, 0)
        水平右方向(1, 0)
        垂直上方向(0, 1)
        垂直下方向 (0, -1)
        左上對角線方向 (-1, 1)
        左下對角線方向 (-1, -1)
        右下對角線方向 (1, -1)
        右上對角線方向 (1, 1)
template <int SIZE>
class QueueSolution : public Object
{
protected:
  enum{N = SIZE + 2};//棋盤大小
  struct Pos : public Object
  {
    Pos(int px = 0, int py = 0):x(px),y(py){}
    int x;
    int y;
    bool operator==(const Pos& other)
    {
        return (this->x = other.x && this->y == other.y);
    }
  };
  int m_chessBoard[N][N];//棋盤數據
  Pos m_direction[3];//方向數據
  LinkedList<Pos> m_solution;//皇后的位置
  int m_count;//解決方案的數量
  void init()
  {
    m_count = 0;
    //初始化棋盤邊界
    for(int x = 0; x < N; x += (N-1))
    {
        for(int y = 0; y < N; y++)
        {
            m_chessBoard[x][y] = 2;//垂直邊界
            m_chessBoard[y][x] = 2;//水平邊界
        }
    }
    //初始化棋盤
    for(int x = 1; x <= SIZE; x++)
    {
      for(int y = 1; y <= SIZE; y++)
      {
        m_chessBoard[x][y] = 0;
      }
    }
    //左下角對角線方向
    m_direction[0] = Pos(-1, -1);
    //垂直向下方向
    m_direction[1] = Pos(0, -1);
    //右下角對角線方向
    m_direction[2] = Pos(1, -1);
  }
  //打印棋盤
  void printBoard()
  {
    for(m_solution.move(0); !m_solution.end(); m_solution.next())
    {
      cout << "(" << m_solution.current().x << "," << m_solution.current().y<< ") ";
    }
    cout << endl;
    for(int x = 0; x < N; x++)
    {
      for(int y = 0; y < N; y++)
      {
          switch (m_chessBoard[x][y])
            {
            case 0:
              cout << " ";
              break;
            case 1:
              cout << "Q";
              break;
            case 2:
              cout << "#";
              break;
            }
      }
      cout << endl;
    }
  }
  bool check(int x, int y, int direction)
  {
    bool flag = true;
    do
    {
        x += m_direction[direction].x;
        y += m_direction[direction].y;
        flag = flag && (m_chessBoard[x][y] == 0);
    }while(flag);
    return (m_chessBoard[x][y] == 2);
  }

  void run(int y)
  {
    if(y < SIZE)
    {
      for(int x = 1; x < SIZE; x++)
      {
        if(check(x,y,0) && check(x,y,1)&&check(x,y,2))
        {
          m_chessBoard[x][y] = 1;
          m_solution.insert(Pos(x, y));
          run(y+1);
          m_chessBoard[x][y] = 0;
          m_solution.remove(m_solution.length() - 1);
        }
      }
    }
    else
    {
      m_count++;
      printBoard();
    }
  }
public:
  QueueSolution()
  {
    init();
  }
  void run()
  {
    run(1);
    cout << "Total:" << m_count << endl;
  }

};
相關文章
相關標籤/搜索