Chapter 5 RECURSION
1,Introduction to Recursion
2,Principles of Recursion
3,Backtracking,Postponing the
Work4,Tree-Structured Programs,
Look-Ahead in Games
5,Pointers and Pitfalls
5.1 Stacks and Trees
数据结构是递归的
THEOREM 5.1 During the traversal of any
tree,vertices are added to or deleted from
the path back to the root in the fashion of a
stack,Given any stack,conversely,a tree
can be drawn to portray the life history of
the stack,as items are
pushed onto or popped from it.
Tree-Diagram Definitions
? The circles in a tree diagram are called
vertices
or nodes.
? The top of the tree is called its root.
? The vertices immediately below a given
vertex
are called the children of that vertex.
?The (unique) vertex immediately above a
given
vertex is called its parent,(The root is the
only
vertex in the tree that has no parent.)
分支
兄弟
? A vertex with no children is called a leaf or
an
external vertex.
? Two branches of a tree are adjacent if the
lower
vertex of the first branch is the upper
vertex of
the second,A sequence of branches in
which
each is adjacent to its successor is called
a path.
?The height of a tree is the number of
vertices on
a longest possible path from the root to a
Factorials,A Recursive
DefinitionInformal definition,The factorial function of a
positive integer is
n! = n?(n-1)?… ?1
Formal definition,?
?
?
???
?
?
时当
时当
1n,1 ) !(nn
0n 1,
n!
定义是递归的
Every recursive process consists of two
parts:
?A smallest,base case that is processed
without
recursion; and
?A general method that reduces a
particular
case to one or more of the smaller cases,
thereby making progress toward
eventually
reducing the problem all the way to the
boundary
recursion
Towers of Hanoi
Rules:
Move only one disk at a time.
No larger disk can be on top of a smaller
disk.
问题的解法是递归的
void move(int count,int start,int finish,int temp);
/* Pre,There are at least count disks on the tower start,
The
top disk (if any) on each of towers temp and finish
is
larger than any of the top count disks on tower
start.
Post,The top count disks on start have been moved to
finish;
temp (used for temporary storage) has been
returned
to its starting position.*/
const int disks = 64;
// Make this constant much smaller to run program.
void move(int count,int start,int finish,int temp);
/* Pre,None.
Post,The simulation of the Towers of Hanoi has
terminated,*/
main( )
{ move(disks,1,3,2); }
void move(int count,int start,int nish,int temp)
{ if (count > 0)
{ move(count - 1,start,temp,nish);
cout << "Move disk " << count << " from " << start
<< " to " << finish << "." << endl;
move(count - 1,temp,nish,start);
}
}
Please see pg,166-167
figure 5.4 - 5.5
5.2 Principles of
Recursion
? Find the key step,Begin by asking yourself,
“How can this problem be divided into
parts?”or
“How will the key step in the middle be done?”
? Find a stopping rule,This stopping rule is
usually the small,special case that is trivial or
easy to handle without recursion.
?Outline your algorithm,Combine the
stopping
rule and the key step,using an if statement
Designing Recursive
Algorithms
?Check termination,Verify that the recursion
will always terminate,Be sure that your
algorithm correctly handles extreme cases.
? Draw a recursion tree,The height of the
tree is
closely related to the amount of memory
that the
program will require,and the total size of
the
tree reflects the number of times the key
step
will be done.
Tail Recursion
DEFINITION Tail recursion occurs when
the
last-executed statement of a function
is a
recursive call to itself.
If the last-executed statement of a function is a
recursive call to the function itself,then this call
can be eliminated by reassigning the calling
parameters to the values specified in the recursive
call,and then repeating the whole function.
Hanoi Without Tail Recursion
void move(int count,int start,int nish,int temp)
/* move, iterative version
Pre,Disk count is a valid disk to be moved.
Post,Moves count disks from start to finish using temp for
temporary storage,*/
{ int swap; //temporary storage to swap towers
while (count > 0) // Replace theif statement with a loop.
{ move(count - 1,start,temp,finish); // first recursive call
cout << "Move disk " << count << " from " << start
<< " to " << finish << "." << endl;
count--; // Change parameters to mimic the second
recursive call.
swap = start; start = temp; temp = swap;
}
}
Calculating Factorials
Please see pg,176-177
Fibonacci Numbers
Please see pg,177-179
5.3 Backtracking
Eight Queens Puzzle
Four Queens Solution
Please see pg.184 fig.5.13
回溯法也称为
状态空间搜索法,
用它可以求出
问题的所有解。
在 8*8的国际象棋上
摆放八个皇后,使其
不能互相攻击,即任意
两个皇后不能处于同一行、
同一列或同一斜线上,
问有多少种摆法。
Solve the problem using recursive,
#include <math.h>
#include <fstream.h>
# define max_board 30
int sum=0; // 计数所得解数目
int x[max_board]; // 存放当前解的向量
int board_size; // 皇后个数
ofstream out(“Queen.out”); // 声明并且打开输出文件流
void Backtrack(int); // 递归回溯法
递归求解
void main(void)
{ cout << "What is the size of the board? ";
cin >> board_size;
if (board_size<0 || board_size>max_board)
cout<<"The number must be between 0 and "
<<max_board<<endl;
else
{ Backtrack(0);
out<<"\nThe number of solution Queen is
"<<sum<<endl;
out.close();
}
}
bool Place(int k) // 检测 k皇后能否放在 x[k]列
{ for(int j=0; j<k; j++)
if((abs(k-j)==abs(x[j]-x[k])) || x[j]==x[k]) return false;
return true;
}
void Backtrack(int i) // 递归回溯法
{ int j;
if(i==board_size) //找到一组解,输出
{ sum++;
for(j=0; j<board_size; j++)out<<" "<<x[j];
out<<endl;
}
for(j=0; j<board_size; j++) //
{ x[i]=j;
if(Place(i))Backtrack(i+1); //确定位置,找下一皇后位置
}
}
Queens Solution (book)
Please see pg.186 - pg.194
Please with the top of solution method
compare
Solve the problem using iterate:
迭代求解
void main()
{ cout << "What is the size of the board? ";
cin >> board_size;
if(board_size<0 || board_size>max_board)
cout<<"The number must be between 0 and "
<<max_board<<endl;
else Backtrack ( );
}
无参数
void Backtrack() //迭代回溯法
{ int k=0,sum=0; x[0]= -1;
while (k >=0 ) // 若 k<0,则已搜索完所有的解,结束回溯
{ x[k]++;
while((x[k]<board_size) && !(Place(k))) x[k]++;
if(x[k]<board_size) // 确定了皇后的位置
if(k==board_size-1) //找到一组解,输出
{ sum++;
for(int i=0; i<board_size; i++)out<<" "<<x[i];
out<<endl;
}
else x[++k]=-1; // k增 1,为下一皇后找安全的位置
else k--; // 回溯
}
out<<"\nThe number of solution Queen is
"<<sum<<endl;
out.close();
}
5.4 Tree-Structured Programs,Look-Ahead in
GamesGame Trees
Please see pg,199
Fig,5.16 Tree for the game of Eight
Please see pg,199 Fig,
5.17
Please see pg,200 Fig,5.18
Tic-Tac-Toe
◆ The tic-tac-toe grid is a 3× 3 array of
integers,with 0 to denote an empty square
and the values 1 and 2 to denote squares
occupied by the first and second players,
respectively.
◆ A Move object stores the coordinates of a
square on the grid.
◆ The Board class contains private data
members to record the current game state in
a 3× 3 array and to record how many moves
have been played.
井字游戏
◆ The game is finished either after nine
moves have been played or when one or the
other player has actually won.
◆ The legal moves available for a player are
just the squares with a value of 0.
◆ Evaluate a Board position as 0 if neither
player has yet won; however,if one or other
player has won,we shall evaluate the
position according to the rule that quick
wins are considered very good,and quick
losses are considered very bad.
◆ A program that sets the depth of look-
ahead to a value of 9 or more will play a
perfect game,since it will always be able to
look ahead to a situation where its
evaluation of the position
is exact,A program with shallower depth
can make mistakes,because it might finish
its look-ahead with a collection of positions
that misleadingly evaluate as zero.pg,204-207
Which classmate would like to Explain the
Program.
Pointers and Pitfalls
◆ Recursion should be used freely in the initial
design of algorithms.It is especially appropriate
where the main step toward solution consists of
reducing a problem to one or more smaller cases.
◆ Study several simple examples to see whether or
not recursion should be used and how it will work.
◆ Attempt to formulate a method that will work
more generally.Ask,“How can this problem be
divided into parts?” or,How will the key step in the
middle be done?”
◆ Ask whether the remainder of the problem can be
done in the same or a similar way,and modify your
method if necessary so that it will be sufficiently
◆ Find a stopping rule that will indicate that the
problem or a suitable part of it is done.
◆ Be very careful that your algorithm always
terminates and handles trivial cases correctly.
◆ The key tool for the analysis of recursive
algorithms is the recursion tree,Draw the
recursion tree for one or two simple examples
appropriate to your problem.
◆ The recursion tree should be studied to see
whether the recursion is needlessly repeating work,
or if the tree represents an efficient division of the
work into pieces.
◆ A recursive function can accomplish exactly the
same tasks as an iterative function using a stack,
Consider carefully whether recursion or iteration
with a stack will lead to a clearer program and give
◆ Tail recursion may be removed if space
considerations are important.
◆ Recursion can always be translated into iteration,
but the general rules will often produce a result
that greatly obscures the structure of the program,
Such obscurity should be tolerated only when the
programming language makes it unavoidable,and
even then it should be well documented.
◆ Study your problem to see if it ts one of the
standard paradigms for recursive algorithms,such
as divide and conquer,backtracking,or tree-
structured algorithms.
◆ Let the use of recursion t the structure of the
problem,When the conditions of the problem are
thoroughly understood,the structure of the
required algorithm will be easier to see.
◆ Always be careful of the extreme cases,Be sure
that your algorithm terminates gracefully when it
reaches the end of its task.
◆ Do as thorough error checking as possible,Be
sure that every condition that a function requires is
stated in its preconditions,and,even so,defend
your function from as many violations of its
preconditions as conveniently possible.
1,Introduction to Recursion
2,Principles of Recursion
3,Backtracking,Postponing the
Work4,Tree-Structured Programs,
Look-Ahead in Games
5,Pointers and Pitfalls
5.1 Stacks and Trees
数据结构是递归的
THEOREM 5.1 During the traversal of any
tree,vertices are added to or deleted from
the path back to the root in the fashion of a
stack,Given any stack,conversely,a tree
can be drawn to portray the life history of
the stack,as items are
pushed onto or popped from it.
Tree-Diagram Definitions
? The circles in a tree diagram are called
vertices
or nodes.
? The top of the tree is called its root.
? The vertices immediately below a given
vertex
are called the children of that vertex.
?The (unique) vertex immediately above a
given
vertex is called its parent,(The root is the
only
vertex in the tree that has no parent.)
分支
兄弟
? A vertex with no children is called a leaf or
an
external vertex.
? Two branches of a tree are adjacent if the
lower
vertex of the first branch is the upper
vertex of
the second,A sequence of branches in
which
each is adjacent to its successor is called
a path.
?The height of a tree is the number of
vertices on
a longest possible path from the root to a
Factorials,A Recursive
DefinitionInformal definition,The factorial function of a
positive integer is
n! = n?(n-1)?… ?1
Formal definition,?
?
?
???
?
?
时当
时当
1n,1 ) !(nn
0n 1,
n!
定义是递归的
Every recursive process consists of two
parts:
?A smallest,base case that is processed
without
recursion; and
?A general method that reduces a
particular
case to one or more of the smaller cases,
thereby making progress toward
eventually
reducing the problem all the way to the
boundary
recursion
Towers of Hanoi
Rules:
Move only one disk at a time.
No larger disk can be on top of a smaller
disk.
问题的解法是递归的
void move(int count,int start,int finish,int temp);
/* Pre,There are at least count disks on the tower start,
The
top disk (if any) on each of towers temp and finish
is
larger than any of the top count disks on tower
start.
Post,The top count disks on start have been moved to
finish;
temp (used for temporary storage) has been
returned
to its starting position.*/
const int disks = 64;
// Make this constant much smaller to run program.
void move(int count,int start,int finish,int temp);
/* Pre,None.
Post,The simulation of the Towers of Hanoi has
terminated,*/
main( )
{ move(disks,1,3,2); }
void move(int count,int start,int nish,int temp)
{ if (count > 0)
{ move(count - 1,start,temp,nish);
cout << "Move disk " << count << " from " << start
<< " to " << finish << "." << endl;
move(count - 1,temp,nish,start);
}
}
Please see pg,166-167
figure 5.4 - 5.5
5.2 Principles of
Recursion
? Find the key step,Begin by asking yourself,
“How can this problem be divided into
parts?”or
“How will the key step in the middle be done?”
? Find a stopping rule,This stopping rule is
usually the small,special case that is trivial or
easy to handle without recursion.
?Outline your algorithm,Combine the
stopping
rule and the key step,using an if statement
Designing Recursive
Algorithms
?Check termination,Verify that the recursion
will always terminate,Be sure that your
algorithm correctly handles extreme cases.
? Draw a recursion tree,The height of the
tree is
closely related to the amount of memory
that the
program will require,and the total size of
the
tree reflects the number of times the key
step
will be done.
Tail Recursion
DEFINITION Tail recursion occurs when
the
last-executed statement of a function
is a
recursive call to itself.
If the last-executed statement of a function is a
recursive call to the function itself,then this call
can be eliminated by reassigning the calling
parameters to the values specified in the recursive
call,and then repeating the whole function.
Hanoi Without Tail Recursion
void move(int count,int start,int nish,int temp)
/* move, iterative version
Pre,Disk count is a valid disk to be moved.
Post,Moves count disks from start to finish using temp for
temporary storage,*/
{ int swap; //temporary storage to swap towers
while (count > 0) // Replace theif statement with a loop.
{ move(count - 1,start,temp,finish); // first recursive call
cout << "Move disk " << count << " from " << start
<< " to " << finish << "." << endl;
count--; // Change parameters to mimic the second
recursive call.
swap = start; start = temp; temp = swap;
}
}
Calculating Factorials
Please see pg,176-177
Fibonacci Numbers
Please see pg,177-179
5.3 Backtracking
Eight Queens Puzzle
Four Queens Solution
Please see pg.184 fig.5.13
回溯法也称为
状态空间搜索法,
用它可以求出
问题的所有解。
在 8*8的国际象棋上
摆放八个皇后,使其
不能互相攻击,即任意
两个皇后不能处于同一行、
同一列或同一斜线上,
问有多少种摆法。
Solve the problem using recursive,
#include <math.h>
#include <fstream.h>
# define max_board 30
int sum=0; // 计数所得解数目
int x[max_board]; // 存放当前解的向量
int board_size; // 皇后个数
ofstream out(“Queen.out”); // 声明并且打开输出文件流
void Backtrack(int); // 递归回溯法
递归求解
void main(void)
{ cout << "What is the size of the board? ";
cin >> board_size;
if (board_size<0 || board_size>max_board)
cout<<"The number must be between 0 and "
<<max_board<<endl;
else
{ Backtrack(0);
out<<"\nThe number of solution Queen is
"<<sum<<endl;
out.close();
}
}
bool Place(int k) // 检测 k皇后能否放在 x[k]列
{ for(int j=0; j<k; j++)
if((abs(k-j)==abs(x[j]-x[k])) || x[j]==x[k]) return false;
return true;
}
void Backtrack(int i) // 递归回溯法
{ int j;
if(i==board_size) //找到一组解,输出
{ sum++;
for(j=0; j<board_size; j++)out<<" "<<x[j];
out<<endl;
}
for(j=0; j<board_size; j++) //
{ x[i]=j;
if(Place(i))Backtrack(i+1); //确定位置,找下一皇后位置
}
}
Queens Solution (book)
Please see pg.186 - pg.194
Please with the top of solution method
compare
Solve the problem using iterate:
迭代求解
void main()
{ cout << "What is the size of the board? ";
cin >> board_size;
if(board_size<0 || board_size>max_board)
cout<<"The number must be between 0 and "
<<max_board<<endl;
else Backtrack ( );
}
无参数
void Backtrack() //迭代回溯法
{ int k=0,sum=0; x[0]= -1;
while (k >=0 ) // 若 k<0,则已搜索完所有的解,结束回溯
{ x[k]++;
while((x[k]<board_size) && !(Place(k))) x[k]++;
if(x[k]<board_size) // 确定了皇后的位置
if(k==board_size-1) //找到一组解,输出
{ sum++;
for(int i=0; i<board_size; i++)out<<" "<<x[i];
out<<endl;
}
else x[++k]=-1; // k增 1,为下一皇后找安全的位置
else k--; // 回溯
}
out<<"\nThe number of solution Queen is
"<<sum<<endl;
out.close();
}
5.4 Tree-Structured Programs,Look-Ahead in
GamesGame Trees
Please see pg,199
Fig,5.16 Tree for the game of Eight
Please see pg,199 Fig,
5.17
Please see pg,200 Fig,5.18
Tic-Tac-Toe
◆ The tic-tac-toe grid is a 3× 3 array of
integers,with 0 to denote an empty square
and the values 1 and 2 to denote squares
occupied by the first and second players,
respectively.
◆ A Move object stores the coordinates of a
square on the grid.
◆ The Board class contains private data
members to record the current game state in
a 3× 3 array and to record how many moves
have been played.
井字游戏
◆ The game is finished either after nine
moves have been played or when one or the
other player has actually won.
◆ The legal moves available for a player are
just the squares with a value of 0.
◆ Evaluate a Board position as 0 if neither
player has yet won; however,if one or other
player has won,we shall evaluate the
position according to the rule that quick
wins are considered very good,and quick
losses are considered very bad.
◆ A program that sets the depth of look-
ahead to a value of 9 or more will play a
perfect game,since it will always be able to
look ahead to a situation where its
evaluation of the position
is exact,A program with shallower depth
can make mistakes,because it might finish
its look-ahead with a collection of positions
that misleadingly evaluate as zero.pg,204-207
Which classmate would like to Explain the
Program.
Pointers and Pitfalls
◆ Recursion should be used freely in the initial
design of algorithms.It is especially appropriate
where the main step toward solution consists of
reducing a problem to one or more smaller cases.
◆ Study several simple examples to see whether or
not recursion should be used and how it will work.
◆ Attempt to formulate a method that will work
more generally.Ask,“How can this problem be
divided into parts?” or,How will the key step in the
middle be done?”
◆ Ask whether the remainder of the problem can be
done in the same or a similar way,and modify your
method if necessary so that it will be sufficiently
◆ Find a stopping rule that will indicate that the
problem or a suitable part of it is done.
◆ Be very careful that your algorithm always
terminates and handles trivial cases correctly.
◆ The key tool for the analysis of recursive
algorithms is the recursion tree,Draw the
recursion tree for one or two simple examples
appropriate to your problem.
◆ The recursion tree should be studied to see
whether the recursion is needlessly repeating work,
or if the tree represents an efficient division of the
work into pieces.
◆ A recursive function can accomplish exactly the
same tasks as an iterative function using a stack,
Consider carefully whether recursion or iteration
with a stack will lead to a clearer program and give
◆ Tail recursion may be removed if space
considerations are important.
◆ Recursion can always be translated into iteration,
but the general rules will often produce a result
that greatly obscures the structure of the program,
Such obscurity should be tolerated only when the
programming language makes it unavoidable,and
even then it should be well documented.
◆ Study your problem to see if it ts one of the
standard paradigms for recursive algorithms,such
as divide and conquer,backtracking,or tree-
structured algorithms.
◆ Let the use of recursion t the structure of the
problem,When the conditions of the problem are
thoroughly understood,the structure of the
required algorithm will be easier to see.
◆ Always be careful of the extreme cases,Be sure
that your algorithm terminates gracefully when it
reaches the end of its task.
◆ Do as thorough error checking as possible,Be
sure that every condition that a function requires is
stated in its preconditions,and,even so,defend
your function from as many violations of its
preconditions as conveniently possible.