আমি মনে করি এই বিষয়টিতে বেশ কয়েকটি প্রশ্ন সমাহিত হয়েছে:
- ও (এন) সময়ে
buildHeapএটি চালিত হয় কীভাবে আপনি বাস্তবায়ন করবেন ?
- সঠিকভাবে প্রয়োগের সময় আপনি কীভাবে ও (এন) সময়ে
buildHeapচলে তা দেখান ?
- ও (এন লগ এন) এর চেয়ে ও (এন ) সময়ে হিপ সাজানোর জন্য একই যুক্তিটি কেন কাজ করে না ?
ও (এন) সময়ে buildHeapএটি চালিত হয় কীভাবে আপনি বাস্তবায়ন করবেন ?
প্রায়শই, এসব প্রশ্নের উত্তর মধ্যে পার্থক্য উপর ফোকাস siftUpএবং siftDown। মধ্যে সঠিক পছন্দ siftUpএবং siftDownপেতে গুরুত্বপূর্ণ হে (ঢ) জন্য কর্মক্ষমতা buildHeapকিন্তু সাহায্যের এক কিছুই মধ্যে পার্থক্য বুঝতে না buildHeapএবং heapSortসাধারণভাবে। বস্তুত, উভয় যথাযথ বাস্তবায়নের buildHeapএবং heapSortহবে শুধুমাত্র ব্যবহার siftDown। siftUpঅপারেশনটি কেবল একটি বিদ্যমান গাদা মধ্যে সন্নিবেশ সম্পাদন করা প্রয়োজন, সুতরাং এটি বাইনারি হিপ ব্যবহার করে অগ্রাধিকারের সারিটি প্রয়োগ করতে ব্যবহৃত হবে, উদাহরণস্বরূপ।
একটি সর্বোচ্চ গাদা কীভাবে কাজ করে তা বর্ণনা করতে আমি এটি লিখেছি। এটি হ্যাপের ধরণটি সাধারণত হিপ বাছাই করার জন্য বা অগ্রাধিকার সারিতে ব্যবহৃত হয় যেখানে উচ্চতর মান উচ্চতর অগ্রাধিকার নির্দেশ করে। একটি মিনিট গাদা এছাড়াও দরকারী; উদাহরণস্বরূপ, বর্ধনক্রমের সাথে পূর্ণসংখ্য কী বা বর্ণমালা অনুসারে স্ট্রিংগুলি আইটেমগুলি পুনরুদ্ধার করার সময়। নীতিগুলি হুবহু একই; কেবল সাজানোর ক্রম পরিবর্তন করুন।
গাদা সম্পত্তি নির্দিষ্ট করে একটি বাইনারি গাদা প্রতিটি নোডের অন্তত তার শিশু দুটি মত বৃহৎ হতে হবে। বিশেষত, এটি বোঝাচ্ছে যে গাদা সবচেয়ে বড় আইটেমটি মূলে রয়েছে। নীচে সরানো এবং উপরে তোলা মূলত বিপরীত দিকগুলিতে একই ক্রিয়াকলাপ: একটি আপত্তিজনক নোডটি সরান যতক্ষণ না এটি স্তূপের সম্পত্তিটি সন্তুষ্ট করে:
siftDown একটি নোডকে অদলবদল করে যা তার বৃহত্তম সন্তানের সাথে খুব ছোট (যার ফলে এটি নীচে সরানো হয়) যতক্ষণ না এটি তার নীচের উভয় নোডের মতো কমপক্ষে বড় না হয়।
siftUp এটির উপরে নোডের চেয়ে বড় না হওয়া অবধি একটি নোড অদলবদল করে যা তার পিতামাতার সাথে খুব বড় (ততক্ষণে এটি সরানো হয়)।
অপারেশন সংখ্যার জন্য প্রয়োজনীয় siftDownএবং siftUpদূরত্ব নোড সরাতে থাকতে পারে সমানুপাতিক। কারণ siftDownএটি গাছের নীচে থেকে দূরত্ব, তাই গাছের siftDownশীর্ষে নোডগুলির জন্য ব্যয়বহুল। সঙ্গে siftUpকাজ গাছ শীর্ষে দূরত্ব সমানুপাতিক, তাই siftUpগাছের নীচে নোড জন্য ব্যয়বহুল। যদিও উভয় ক্রিয়াকলাপ সবচেয়ে খারাপ ক্ষেত্রে ও (লগ এন) হয় তবে একটি স্তূপে কেবল একটি নোড শীর্ষে থাকে যেখানে অর্ধেক নোড নীচের স্তরে থাকে। সুতরাং এটি খুব অবাক হওয়ার কিছু নেই যে আমাদের যদি প্রতিটি নোডে একটি অপারেশন প্রয়োগ করতে হয় তবে আমরা তার siftDownচেয়ে বেশি পছন্দ করব siftUp।
buildHeapফাংশন যতক্ষণ না তারা সবাই গাদা সম্পত্তি সন্তুষ্ট যার ফলে একটি বৈধ গাদা উত্পাদক পাঁচমিশালী আইটেম এবং তাদের প্যাচসমূহ একটি অ্যারের লাগে। আমাদের বর্ণিত এবং ক্রিয়াকলাপগুলি buildHeapব্যবহারের জন্য দুটি গ্রহণ করতে পারে ।siftUpsiftDown
স্তূপের শীর্ষে (অ্যারের শুরু) শুরু করুন এবং siftUpপ্রতিটি আইটেমটিতে কল করুন। প্রতিটি পদক্ষেপে, পূর্ববর্তী চালিত আইটেমগুলি (অ্যারেতে বর্তমান আইটেমের আগে থাকা আইটেমগুলি) একটি বৈধ গাদা গঠন করে এবং পরবর্তী আইটেমটি সরিয়ে রাখলে এটি গাদাতে একটি বৈধ অবস্থানে রাখে। প্রতিটি নোড আপ করার পরে, সমস্ত আইটেম গাদা সম্পত্তি সন্তুষ্ট।
অথবা, বিপরীত দিকে যান: অ্যারের শেষে শুরু করুন এবং সামনের দিকে পিছন দিকে যান। প্রতিটি পুনরাবৃত্তির সময়, আপনি কোনও আইটেমটি সঠিক স্থানে না হওয়া পর্যন্ত নীচে রেখে দিন।
কোন বাস্তবায়নের জন্য buildHeapআরও দক্ষ?
এই উভয় সমাধানই একটি বৈধ গাদা উত্পাদন করবে। আশ্চর্যজনকভাবে, আরও কার্যকর একটি হ'ল দ্বিতীয় অপারেশন যা ব্যবহার করে siftDown।
আসুন h = লগ এন গাদা উচ্চতা উপস্থাপন করুন। siftDownপদ্ধতির জন্য প্রয়োজনীয় কাজটি যোগফল দ্বারা দেওয়া হয়
(0 * n/2) + (1 * n/4) + (2 * n/8) + ... + (h * 1).
যোগফলের প্রতিটি পদটিতে প্রদত্ত উচ্চতায় নোডের সর্বাধিক দূরত্ব থাকে (নীচের স্তরের জন্য শূন্য, মূলের জন্য এইচ) যে উচ্চতায় নোডের সংখ্যা দ্বারা গুণিত হয়। বিপরীতে, siftUpপ্রতিটি নোডে কল করার যোগফল
(h * n/2) + ((h-1) * n/4) + ((h-2)*n/8) + ... + (0 * 1).
এটি পরিষ্কার হওয়া উচিত যে দ্বিতীয় যোগফলটি আরও বড়। প্রথম শব্দটি একা hn / 2 = 1/2 n লগ এন , সুতরাং এই পদ্ধতির সেরা ও (এন লগ এন) এ জটিলতা রয়েছে ।
আমরা siftDownপদ্ধতির যোগফল কীভাবে প্রমাণ করব যে সত্যই ও (এন) ?
একটি পদ্ধতি (এমন অন্যান্য বিশ্লেষণও রয়েছে যা কাজ করে) হ'ল সীমাবদ্ধ অঙ্ককে অসীম সিরিজে রূপান্তর করা এবং তারপরে টেলর সিরিজ ব্যবহার করা। আমরা প্রথম পদটি উপেক্ষা করতে পারি, যা শূন্য:

যদি আপনি নিশ্চিত হন না যে এই পদক্ষেপগুলির প্রত্যেকটি কেন কাজ করে, তবে কথায় কথায় প্রক্রিয়াটির ন্যায্যতা এখানে রয়েছে:
- পদগুলি সমস্ত ইতিবাচক, সুতরাং সসীম যোগফল অবশ্যই অসীমের যোগফলের চেয়ে ছোট হতে হবে।
- সিরিজটি x = 1/2 এ মূল্যায়ন করা পাওয়ার সিরিজের সমান ।
- সেই পাওয়ার সিরিজটি f (x) = 1 / (1-x) এর জন্য টেলর সিরিজের ডেরিভেটিভ (একটি ধ্রুবক বারের) সমান ।
- x = 1/2 Tay টেলর সিরিজের রূপান্তরকরণের ব্যবধানের মধ্যে।
- অতএব, আমরা টেইলর সিরিজটিকে 1 / (1-x) দিয়ে প্রতিস্থাপন করতে পারি , পার্থক্য করতে পারি এবং অসীম সিরিজের মানটি নির্ধারণ করতে পারি।
যেহেতু অসীম যোগফলটি হ'ল এন , আমরা এই সিদ্ধান্তে পৌঁছলাম যে সীমাবদ্ধ যোগফল বড় নয়, এবং তাই (ও) ।
কেন হিপ সাজানোর জন্য ও (এন লগ এন) সময় প্রয়োজন?
যদি buildHeapরৈখিক সময়ে চালানো সম্ভব হয় তবে কেন হিপ সাজানোর জন্য ও (এন লগ এন) সময় প্রয়োজন? ওয়েল, হিপ সাজানোর দুটি স্তর রয়েছে। প্রথমে আমরা buildHeapঅ্যারেটিতে কল করি , যার জন্য সর্বোত্তমভাবে প্রয়োগ করা হলে ও (এন) সময় প্রয়োজন । পরবর্তী পর্যায়ে হ'ল বারবার গাদা বৃহত্তম আইটেম মুছে ফেলা এবং অ্যারের শেষে এটি রাখা। যেহেতু আমরা গাদা থেকে কোনও আইটেম মুছি, সেখানে গাদা শেষ হওয়ার ঠিক পরে যেখানে আমরা আইটেমটি সংরক্ষণ করতে পারি সেখানে সর্বদা একটি খোলা জায়গা থাকে। সুতরাং হিপ সাজানোর ক্রমানুসারে পরবর্তী বৃহত্তম আইটেমটি সরিয়ে এবং শেষ অবস্থানে শুরু করে অ্যারেতে রেখে সম্মুখের দিকে অগ্রসর করে একটি সাজানো অর্ডার অর্জন করে। এটি এই শেষ অংশটির জটিলতা যা হ্যাপ সাজানোর ক্ষেত্রে প্রাধান্য দেয়। লুপটি দেখতে পছন্দ করে:
for (i = n - 1; i > 0; i--) {
arr[i] = deleteMax();
}
স্পষ্টতই, লুপটি O (n) বার চালায় ( সুনির্দিষ্ট হতে n - 1 , শেষ আইটেমটি ইতিমধ্যে স্থানে রয়েছে)। deleteMaxগাদা করার জটিলতা হ'ল ও (লগ এন) । এটি সাধারণত মূলটি (গাদা থেকে ফেলে রাখা বৃহত্তম আইটেম) মুছে ফেলা এবং এটি স্তূপের শেষ আইটেমের সাথে প্রতিস্থাপন করে প্রয়োগ করা হয়, যা একটি পাত, এবং সেইজন্য ক্ষুদ্রতম আইটেমগুলির মধ্যে একটি। এই নতুন রুটটি অবশ্যই হ'ল সম্পত্তি লঙ্ঘন করবে, সুতরাং আপনি siftDownএটি গ্রহণযোগ্য অবস্থানে না নিয়ে যাওয়া পর্যন্ত আপনাকে কল করতে হবে। এটি পরবর্তী বৃহত্তম আইটেমটিকে মূল পর্যন্ত উপরে নিয়ে যাওয়ার প্রভাবও রাখে। লক্ষ্য করুন যে, গাছের নীচ থেকে buildHeapআমরা যে নোডের জন্য কল করছি তার বিপরীতে siftDownআমরা এখন siftDownপ্রতিটি পুনরাবৃত্তিতে গাছের শীর্ষ থেকে কল করছি !যদিও গাছটি সঙ্কুচিত হচ্ছে, এটি পর্যাপ্ত পরিমাণে সঙ্কুচিত হয় না : আপনি নোডগুলির প্রথমার্ধটি সরিয়ে না দেওয়া পর্যন্ত গাছের উচ্চতা স্থির থাকে (যখন আপনি নীচের স্তরটি সম্পূর্ণভাবে সরিয়ে ফেলবেন)। তারপরে পরবর্তী প্রান্তিকের জন্য, উচ্চতা h - 1 হবে । সুতরাং এই দ্বিতীয় পর্যায়ে মোট কাজ
h*n/2 + (h-1)*n/4 + ... + 0 * 1.
স্যুইচটি লক্ষ্য করুন: এখন শূন্য কাজের ক্ষেত্রে একটি নোডের সাথে সম্পর্কিত এবং এইচ ওয়ার্ক কেসটি অর্ধ নোডের সাথে মিলে যায়। এই যোগফলটি হ'ল (এন লগ এন) এরbuildHeap অদৃশ্য সংস্করণটির মতো সিফটআপ ব্যবহার করে প্রয়োগ করা হয়। তবে এই ক্ষেত্রে, আমাদের কোনও বিকল্প নেই যেহেতু আমরা বাছাইয়ের চেষ্টা করছি এবং আমাদের পরবর্তী বড় আইটেমটি পরের অপসারণের প্রয়োজন।
সংক্ষেপে, হিপ সাজানোর জন্য কাজটি দুটি স্তরের যোগফল: বিল্ডহীপ এবং ও (এন লগ এন) এর জন্য প্রতিটি নোডকে যথাযথভাবে সরিয়ে ফেলার জন্য ও (এন) সময় , সুতরাং জটিলতা হ'ল (এন লগ এন) । আপনি প্রমাণ করতে পারেন (তথ্য তত্ত্ব থেকে কিছু ধারণা ব্যবহার করে) যে তুলনা-ভিত্তিক সাজানোর জন্য, ও (এন লগ এন) যেভাবেই আপনি আশা করতে পারেন সেরা, সুতরাং এটি দ্বারা হতাশ হওয়ার কোনও কারণ নেই বা অর্জনের জন্য হিপ সাজানোর আশা করা উচিত নয় ও (এন) সময় সীমাবদ্ধ তা buildHeapকরে।