- vừa được xem lúc

Leetcode 295 - Find the Kth Largest Element in an Array in Tiếng Việt

0 0 4

Người đăng: Văn Lê Đình

Theo Viblo Asia

Yêu cầu bài toán

Cho một array và một số nguyên k, trả về phần tử lớn thứ k trong mảng. Ngoài ra bài toán này còn yêu cầu là không được xài sắp xếp.

Quick Select

Quick select là gì?

Quick select là một thuật toán sử dụng trong việc tìm kiếm một phần tử cụ thể trong một danh sách không được sắp xếp. Nó được sử dụng để tìm kiếm phần tử thứ k trong một danh sách gồm n phần tử, trong đó k thường là một số nguyên từ 1 đến n. Quick select dựa trên thuật toán chia để trị, sử dụng nguyên tắc chọn một phần tử chốt (pivot) và chia danh sách thành hai phần, một bên nhỏ hơn pivot và một bên lớn hơn pivot. Dựa vào giá trị k so với vị trí của pivot, thuật toán quyết định tiếp tục tìm kiếm ở phần nào của danh sách.

Cách sử dụng Quick select:

  • Chọn một phần tử chốt (pivot) từ danh sách. Thông thường, bạn có thể chọn phần tử ở giữa danh sách, nhưng lưu ý là lựa chọn pivot có thể ảnh hưởng đến hiệu suất của thuật toán.
  • Chia danh sách thành hai phần, một phần nhỏ hơn pivot và một phần lớn hơn pivot. Sắp xếp các phần này.
  • So sánh vị trí của pivot với giá trị k mà bạn muốn tìm kiếm. Nếu pivot nằm ở vị trí k, bạn đã tìm thấy phần tử cần.
  • Nếu k lớn hơn vị trí của pivot, tìm kiếm ở phần danh sách lớn hơn pivot. Nếu k nhỏ hơn vị trí của pivot, tìm kiếm ở phần danh sách nhỏ hơn pivot.
  • Lặp lại quá trình này bằng cách thu hẹp phạm vi tìm kiếm cho đến khi bạn tìm thấy phần tử cần tìm.

Triển khai

Ok sau khi tìm hiểu thì chúng ta sẽ code thôi

public class Solution { private static readonly Random rand = new(); public int FindKthLargest(int[] nums, int k) { int result = Select(nums, 0, nums.Length - 1, nums.Length - k); return result; } private int Select(int[] nums, int left, int right, int kSmallest) { if (left == right) { return nums[left]; } int pivotIndex = rand.Next(left, right); pivotIndex = Partition(nums, left, right, pivotIndex); if (kSmallest == pivotIndex) { return nums[kSmallest]; } else if (kSmallest < pivotIndex) { return Select(nums, left, pivotIndex - 1, kSmallest); } else { return Select(nums, pivotIndex + 1, right, kSmallest); } } private int Partition(int[] nums, int left, int right, int pivotIndex) { int pivot = nums[pivotIndex]; Swap(nums, pivotIndex, right); int storeIndex = left; for (int i = left; i <= right; i++) { if (nums[i] < pivot) { Swap(nums, storeIndex, i); storeIndex++; } } Swap(nums, storeIndex, right); return storeIndex; } private void Swap(int[] nums, int a, int b) { int tmp = nums[a]; nums[a] = nums[b]; nums[b] = tmp; }
}

Giải thích

  1. Có ai thắc mắc rằng ở trong hàm FindKthLargest, khi gọi hàm Select thì ta phải pass value là nums.Length - k ở param cuối không?
    • Sở dĩ ta pass giá trị nums.Length - k chứ không phải pass k vào là vì ta đang tìm phần tử lớn nhất thứ k chứ không phải phần tử bé nhất thứ k.
    • Chúng ta đang thực hành việc sắp xếp phần tử bé ở bên trái pivot, phần tử lớn ở bên phải pivot. Vậy thì không thể chỉ vì muốn tìm phần tử lớn thứ k mà ta phải đổi cách thực hiện bằng cách đem phần tử bé hơn ở bên phải và phần tử lớn hơn ở bên trái được. Hãy thực hành cho đúng trước rồi khi rành thì muốn biến tấu sao cũng được nha các bạn.
    • Sau khi mình nêu ra 2 quan điểm ở trên, nếu các bạn vẫn chưa hiểu thì lấy 1 tờ giấy ra, viết 1 cái mảng nào đó, chọn 1 phần tử k nào đó được sắp xếp tăng dần và lấy độ dài mảng trừ đi k xem có phải cái kết quả đó chính là phần tử lớn thứ k hay không?
    • Bài toán yêu cầu là tìm phần tử lớn thứ k đúng không? Nhưng suy ra chúng ta cũng có thể dịch ngược yêu cầu của bài toán này thành, tìm phần tử bé thứ nums.Length - k trong mảng để dễ thực hiện với Quick select hơn nha.
  2. Có ai thắc mắc rằng ở trong hàm Partition, tại sao ta phải hoán đổi vị trí của pivot với vị trí bên phải ngoài cùng trước khi thực hiện việc phân vùng không?
    • Lý do hoán đổi pivot với phần tử ngoài cùng bên phải khi bắt đầu quá trình phân vùng là để không cho cái thằng pivot bị ảnh hưởng trong quá trình phân vùng.
    • Việc phân vùng ảnh hưởng tới việc di chuyển các phần tử xung quanh trong mảng dựa trên việc chúng nhỏ hơn hay lớn hơn pivot. Nếu pivot nằm ở giữa mảng thì pivot vẫn bị ảnh hưởng, pivot bị di chuyển trong lúc phân vùng -> gây ra phức tạp trong quá trình phân vùng.
    • Di chuyển pivot về phần tử cuối cùng giúp đảm bảo rằng nó vẫn ở nguyên vị trí pivot trong khi những phần tử khác vẫn được sắp xếp. Sau khi phân vùng xong thì sẽ di chuyển pivot lại từ vị trí cuối cùng của nó tới vị trí của storeIndex đang trỏ đến. (Do đó các bạn hiểu storeIndex làm gì ha).

Bình luận

Bài viết tương tự

- vừa được xem lúc

Thuật toán quay lui (Backtracking)

Quay lui là một kĩ thuật thiết kế giải thuật dựa trên đệ quy. Ý tưởng của quay lui là tìm lời giải từng bước, mỗi bước chọn một trong số các lựa chọn khả dĩ và đệ quy.

0 0 38

- vừa được xem lúc

Các thuật toán cơ bản trong AI - Phân biệt Best First Search và Uniform Cost Search (UCS)

Nếu bạn từng đọc các thuật toán trong AI (Artificial Intelligence - Trí tuệ nhân tạo), rất có thể bạn từng nghe qua về các thuật toán tìm kiếm cơ bản: UCS (thuộc chiến lược tìm kiếm mù) và Best First Search (thuộc chiến lược tìm kiếm kinh nghiệm). Khác nhau rõ từ khâu phân loại rồi, thế nhưng hai th

0 0 152

- vừa được xem lúc

Sử dụng vector trong lập trình C++ - giải bài toán lập trình muôn thủa

Chào buổi tối mọi người, hôm nay lang thang trên mạng bắt gặp bài toán quen thuộc một thời của quãng đường sinh viên IT. Đấy chính là câu số 1 trong đề thi dưới đây:.

0 0 36

- vừa được xem lúc

MÔ PHỎNG THUẬT TOÁN VƯƠNG HẠO TRONG PROLOG

. 1. Các luật suy diễn trong thuật toán Vương Hạo. Luật 1: Chuyển vế các giả thuyết và kết luận ở dạng phủ định. Ví dụ: p v q, !(r ^ s), !q, p v r -> s, !p <=> p v q, p v r, p -> s, r ^ s, q.

0 0 77

- vừa được xem lúc

A* Search Algorithm

What is A* Search Algorithm. How it works. . Explanation.

0 0 43

- vừa được xem lúc

Python: Jump Search

Search là một từ khóa khá là quen thuộc đối với chúng ta. Hiểu theo đúng nghĩa đen của nó chính là "Tìm kiếm".

0 0 36