- 深度優先搜索DFS
DFS就是回溯法,用遞歸的方法是很自然的。那么該如何遞歸呢?
簡單的說就是:1、如果當前節點沒有被搜索過,那么處理當前節點,並標記為搜索過;如果當前節點已經被搜索過,退出;
2、遞歸遍歷所有沒有被搜索過的臨接節點。
注意,第一步的退出條件。遞歸必須有退出條件,否則會出現死循環。
對任意節點調用上述DFS函數,將搜索到所有和該節點聯通的節點。
- 非遞歸方式實現DFS
遞歸和棧總是聯系在一起的,如果不采用遞歸,那么就需要自己維護一個棧。
1、從某節點開始,入棧;
2、當棧不為空時,循環3、4;當棧為空時,退出循環;
3、對棧頂節點處理,標記為搜索過;注意,如果該節點的某個臨接節點處理完后,會回溯到該節點,注意不要重復處理該節點。
4、對棧頂節點,任意找一個沒有搜索過的臨接節點,入棧(注意只入棧一個臨接節點,DFS就是找到一條路一直走下去,走不通了再回溯);如果發現該棧頂節點所有的臨接節點都被搜索過,或者該節點沒有臨接節點,將該棧頂節點出棧。
- 實現代碼
下面是DFS的遞歸和非遞歸的代碼,C實現。為了簡單起見,搜索對象為下圖所示的滿二叉樹,用數組表示。
1 #include<stdio.h> 2 #include<string.h> 3 4 #define true 1 5 #define false 0 6 #define ok 0 7 #define error -1 8 9 #define MAX_LEN 20 10 #define TREE_LEN 10 11 12 typedef struct 13 { 14 int arr[MAX_LEN]; 15 int top; 16 }STACK; 17 18 int is_empty(STACK *s) 19 { 20 if(s == NULL ) 21 { 22 printf("null param.\n"); 23 return error; 24 } 25 if(0 == s->top) 26 return true; 27 else 28 return false; 29 } 30 31 int is_full(STACK *s) 32 { 33 if(s == NULL ) 34 { 35 printf("null param.\n"); 36 return error; 37 } 38 if( MAX_LEN == s->top) 39 return true; 40 else 41 return false; 42 } 43 void push(STACK* s,int elem) 44 { 45 if(s == NULL ) 46 { 47 printf("null param.\n"); 48 return; 49 } 50 51 if(is_full(s)) 52 { 53 printf("is full.\n"); 54 return; 55 } 56 57 s->arr[s->top++] = elem; 58 } 59 60 void pop(STACK* s) 61 { 62 if(s == NULL ) 63 { 64 printf("null param.\n"); 65 return; 66 } 67 68 if(is_empty(s)) 69 { 70 printf("is empty.\n"); 71 return; 72 } 73 74 s->arr[--s->top] = 0; 75 } 76 77 int top(STACK* s) 78 { 79 if(s == NULL ) 80 { 81 printf("null param.\n"); 82 return error; 83 } 84 85 if(is_empty(s)) 86 { 87 printf("is full.\n"); 88 return error; 89 } 90 91 return s->arr[s->top-1]; 92 } 93 94 void dfs(STACK* p_stack,int *p_tree,int tree_len) 95 { 96 int visited[tree_len]; 97 int temp = 0; 98 int treeIndex = 0; 99 100 memset(visited,0,sizeof(visited)); 101 102 push(p_stack,p_tree[treeIndex]); 103 while(!is_empty(p_stack)) 104 { 105 temp = top(p_stack); 106 107 if(treeIndex < tree_len && !visited[treeIndex]) 108 { 109 printf("%d->",temp); 110 visited[treeIndex] = true; 111 } 112 113 if(2*treeIndex+1 < tree_len && !visited[2*treeIndex+1]) 114 { 115 push(p_stack,p_tree[2*treeIndex+1]); 116 treeIndex = 2*treeIndex+1; 117 } 118 else if( 2*treeIndex+2 < tree_len && !visited[2*treeIndex+2] ) 119 { 120 push(p_stack,p_tree[2*treeIndex+2]); 121 treeIndex = 2*treeIndex+2; 122 } 123 else 124 { 125 pop(p_stack); 126 treeIndex = (treeIndex-1)>>1; 127 } 128 } 129 printf("\n"); 130 } 131 132 133 void dfs_reverse(int *p_tree,int*p_visited,int tree_len,int treeIndex) 134 { 135 int left,right = 0; 136 137 if(treeIndex < tree_len && !p_visited[treeIndex]) 138 { 139 printf("%d->",p_tree[treeIndex]); 140 p_visited[treeIndex] = true; 141 142 left = 2*treeIndex+1; 143 if( left < tree_len && !p_visited[left]) 144 dfs_reverse(p_tree,p_visited,tree_len,left); 145 146 right = 2*treeIndex+2; 147 if( right < tree_len && !p_visited[right]) 148 dfs_reverse(p_tree,p_visited,tree_len,right); 149 } 150 } 151 152 153 int tree[TREE_LEN] = {1,2,3,4,5,6,7,8,9,10}; 154 STACK stack ; 155 156 #ifdef REVERSE 157 int main() 158 { 159 int visited[TREE_LEN] = {0}; 160 memset(visited,0,sizeof(visited)); 161 162 dfs_reverse(tree,visited,TREE_LEN,0); 163 } 164 #else 165 int main() 166 { 167 memset(&stack,0,sizeof(stack)); 168 dfs(&stack,tree,TREE_LEN); 169 } 170 #endif
編譯運行結果:
可以看出結果是符合預期的。代碼中對左右子樹的處理,是對臨接節點處理的一種特殊情況。