मैंने उन दृष्टिकोणों के बीच एक और अंतर पाया। यह सरल और महत्वहीन लगता है, लेकिन साक्षात्कारों की तैयारी करते समय इसकी बहुत महत्वपूर्ण भूमिका होती है और यह विषय उठता है, इसलिए बारीकी से देखें।
संक्षेप में: 1) पुनरावृति पश्च-क्रम अनुक्रमण आसान नहीं है - जो डीएफटी को और अधिक जटिल बनाता है 2) पुनरावृत्ति के साथ चक्रों को आसान बनाता है
विवरण:
पुनरावर्ती मामले में, पूर्व और पोस्ट ट्रैवर्सल्स बनाना आसान है:
एक सुंदर मानक प्रश्न की कल्पना करें: "सभी कार्यों को प्रिंट करें जिन्हें कार्य 5 को निष्पादित करने के लिए निष्पादित किया जाना चाहिए, जब कार्य अन्य कार्यों पर निर्भर करते हैं"
उदाहरण:
//key-task, value-list of tasks the key task depends on
//"adjacency map":
Map<Integer, List<Integer>> tasksMap = new HashMap<>();
tasksMap.put(0, new ArrayList<>());
tasksMap.put(1, new ArrayList<>());
List<Integer> t2 = new ArrayList<>();
t2.add(0);
t2.add(1);
tasksMap.put(2, t2);
List<Integer> t3 = new ArrayList<>();
t3.add(2);
t3.add(10);
tasksMap.put(3, t3);
List<Integer> t4 = new ArrayList<>();
t4.add(3);
tasksMap.put(4, t4);
List<Integer> t5 = new ArrayList<>();
t5.add(3);
tasksMap.put(5, t5);
tasksMap.put(6, new ArrayList<>());
tasksMap.put(7, new ArrayList<>());
List<Integer> t8 = new ArrayList<>();
t8.add(5);
tasksMap.put(8, t8);
List<Integer> t9 = new ArrayList<>();
t9.add(4);
tasksMap.put(9, t9);
tasksMap.put(10, new ArrayList<>());
//task to analyze:
int task = 5;
List<Integer> res11 = getTasksInOrderDftReqPostOrder(tasksMap, task);
System.out.println(res11);**//note, no reverse required**
List<Integer> res12 = getTasksInOrderDftReqPreOrder(tasksMap, task);
Collections.reverse(res12);//note reverse!
System.out.println(res12);
private static List<Integer> getTasksInOrderDftReqPreOrder(Map<Integer, List<Integer>> tasksMap, int task) {
List<Integer> result = new ArrayList<>();
Set<Integer> visited = new HashSet<>();
reqPreOrder(tasksMap,task,result, visited);
return result;
}
private static void reqPreOrder(Map<Integer, List<Integer>> tasksMap, int task, List<Integer> result, Set<Integer> visited) {
if(!visited.contains(task)) {
visited.add(task);
result.add(task);//pre order!
List<Integer> children = tasksMap.get(task);
if (children != null && children.size() > 0) {
for (Integer child : children) {
reqPreOrder(tasksMap,child,result, visited);
}
}
}
}
private static List<Integer> getTasksInOrderDftReqPostOrder(Map<Integer, List<Integer>> tasksMap, int task) {
List<Integer> result = new ArrayList<>();
Set<Integer> visited = new HashSet<>();
reqPostOrder(tasksMap,task,result, visited);
return result;
}
private static void reqPostOrder(Map<Integer, List<Integer>> tasksMap, int task, List<Integer> result, Set<Integer> visited) {
if(!visited.contains(task)) {
visited.add(task);
List<Integer> children = tasksMap.get(task);
if (children != null && children.size() > 0) {
for (Integer child : children) {
reqPostOrder(tasksMap,child,result, visited);
}
}
result.add(task);//post order!
}
}
ध्यान दें कि पुनरावर्ती पोस्ट-ऑर्डर-ट्रैवर्सल को परिणाम के बाद के उलट की आवश्यकता नहीं होती है। पिछले छपे प्रश्न में बच्चे पहले और आपके टास्क छपे। सब कुछ ठीक है। आप एक पुनरावर्ती पूर्व-आदेश-ट्रैवर्सल (जो ऊपर भी दिखाया गया है) कर सकते हैं और किसी को परिणाम सूची के उलटने की आवश्यकता होगी।
पुनरावृत्ति दृष्टिकोण के साथ इतना आसान नहीं है! पुनरावृत्ति (एक स्टैक) दृष्टिकोण में आप केवल एक पूर्व-आदेश-ट्रैवर्सल कर सकते हैं, इसलिए आप अंत में परिणाम सरणी को उलटने के लिए बाध्य हैं:
List<Integer> res1 = getTasksInOrderDftStack(tasksMap, task);
Collections.reverse(res1);//note reverse!
System.out.println(res1);
private static List<Integer> getTasksInOrderDftStack(Map<Integer, List<Integer>> tasksMap, int task) {
List<Integer> result = new ArrayList<>();
Set<Integer> visited = new HashSet<>();
Stack<Integer> st = new Stack<>();
st.add(task);
visited.add(task);
while(!st.isEmpty()){
Integer node = st.pop();
List<Integer> children = tasksMap.get(node);
result.add(node);
if(children!=null && children.size() > 0){
for(Integer child:children){
if(!visited.contains(child)){
st.add(child);
visited.add(child);
}
}
}
//If you put it here - it does not matter - it is anyway a pre-order
//result.add(node);
}
return result;
}
सरल लगता है, नहीं?
लेकिन यह कुछ साक्षात्कारों में एक जाल है।
इसका अर्थ निम्नलिखित है: पुनरावर्ती दृष्टिकोण के साथ, आप गहराई पहले ट्रैवर्सल को लागू कर सकते हैं और फिर चयन कर सकते हैं कि आपको किस क्रम में पूर्व या पोस्ट की आवश्यकता है (बस "प्रिंट" के स्थान को बदलकर, "परिणाम सूची में जोड़ने" के हमारे मामले में) )। पुनरावृत्ति (एक स्टैक) दृष्टिकोण के साथ आप आसानी से केवल पूर्व-क्रम ट्रैवर्सल कर सकते हैं और इसलिए उस स्थिति में जब बच्चों को पहले मुद्रित करने की आवश्यकता होती है (बहुत सारी स्थिति जब आपको नीचे के नोड्स से प्रिंट शुरू करने की आवश्यकता होती है, ऊपर की तरफ जा रहे हैं) - आप में हैं मुसीबत। यदि आपके पास वह परेशानी है जो आप बाद में उलट सकते हैं, लेकिन यह आपके एल्गोरिथ्म के लिए एक अतिरिक्त होगा। और अगर एक साक्षात्कारकर्ता अपनी घड़ी को देख रहा है तो यह आपके लिए एक समस्या हो सकती है। पुनरावृत्ति के बाद के ट्रैवर्सल के जटिल तरीके हैं, वे मौजूद हैं, लेकिन वे सरल नहीं हैं । उदाहरण:https://www.geeksforgeeks.org/iterative-postorder-traversal-using-stack/
इस प्रकार, निचला रेखा: मैं साक्षात्कार के दौरान पुनरावृत्ति का उपयोग करूंगा, इसे प्रबंधित करना और व्याख्या करना सरल है। आपके पास किसी भी जरूरी मामले में प्री-ऑर्डर से पहले आने-जाने का आसान तरीका है। पुनरावृत्ति के साथ आप लचीले नहीं हैं।
मैं पुनरावृत्ति का उपयोग करता हूं और फिर बताता हूं: "ठीक है, लेकिन पुनरावृत्त मुझे इस्तेमाल की गई स्मृति पर अधिक प्रत्यक्ष नियंत्रण प्रदान कर सकता है, मैं आसानी से स्टैक आकार को माप सकता हूं और कुछ खतरनाक अतिप्रवाह को हटा सकता हूं .."
पुनरावृत्ति का एक और प्लस - यह एक ग्राफ में चक्र से बचने / नोटिस करने के लिए सरल है।
उदाहरण (प्रुडोकोड):
dft(n){
mark(n)
for(child: n.children){
if(marked(child))
explode - cycle found!!!
dft(child)
}
unmark(n)
}