CodeGym /Blog Java /Ngẫu nhiên /Thuật toán sắp xếp trên lý thuyết và trong thực tế

Thuật toán sắp xếp trên lý thuyết và trong thực tế

Xuất bản trong nhóm
Sắp xếp là một trong những thao tác cơ bản mà chúng ta thực hiện trên các đối tượng. Ngay cả khi còn nhỏ, trẻ em được dạy sắp xếp khi chúng phát triển kỹ năng tư duy. Máy tính và phần mềm cũng không ngoại lệ. Có rất nhiều thuật toán sắp xếp trong Java . Tôi khuyên bạn nên kiểm tra chúng là gì và chúng hoạt động như thế nào. Điều gì sẽ xảy ra nếu bạn được hỏi về một trong số họ tại một cuộc phỏng vấn vào một ngày nào đó?

Giới thiệu

Sắp xếp các phần tử là một trong những loại thuật toán mà nhà phát triển phải biết. Nếu khoa học máy tính đã từng không được coi trọng khi tôi còn đi học, thì học sinh ngày nay phải có khả năng thực hiện và hiểu các thuật toán sắp xếp. Các thuật toán cơ bản, đơn giản nhất, được thực hiện bằng vòng lặp for . Đương nhiên, để sắp xếp một tập hợp các phần tử, chẳng hạn như một mảng, bằng cách nào đó bạn cần duyệt qua tập hợp đó. Ví dụ:

int[] array = {10, 2, 10, 3, 1, 2, 5};
for (int i = 0; i < array.length; i++) {
	System.out.println(array[i]);
}
Có thể nói gì về đoạn mã này? Chúng ta có một vòng lặp trong đó chúng ta thay đổi chỉ số ( int i) từ 0 thành phần tử cuối cùng trong mảng. Trên thực tế, chúng ta chỉ đơn giản là lấy từng phần tử trong mảng và in nội dung của nó. Càng nhiều phần tử trong mảng, mã sẽ mất nhiều thời gian hơn để hoàn thành. Đó là, nếu nlà số lượng phần tử, khi đó n = 10chương trình sẽ mất gấp đôi thời gian để chạy so với khi n = 5. Nếu chương trình của chúng ta có một vòng lặp duy nhất, thời gian thực hiện sẽ tăng theo tuyến tính: càng có nhiều phần tử thì thời gian thực hiện càng dài. Hóa ra thuật toán trên hoạt động trong thời gian tuyến tính (một hàm tuyến tính của n). Trong những trường hợp như vậy, chúng ta nói rằng độ phức tạp của thuật toán là "O(n)". Ký hiệu này còn được gọi là "big O" hoặc "hành vi tiệm cận". Nhưng bạn chỉ có thể nhớ "

Thuật toán sắp xếp đơn giản nhất (sắp xếp bong bóng)

Giả sử chúng ta có một mảng và chúng ta có thể lặp qua nó. Tuyệt vời. Bây giờ hãy thử sắp xếp nó theo thứ tự tăng dần. Điều đó có nghĩa là gì? Có nghĩa là đã cho 2 phần tử (ví dụ , a = 6) b = 5, chúng ta phải sắp xếp lại abif alớn hơn b(if a > b). Điều này có nghĩa là gì khi chúng ta đang sử dụng một chỉ mục để làm việc với một bộ sưu tập (như trường hợp của một mảng)? Nghĩa là nếu phần tử có chỉ số a lớn hơn phần tử có chỉ số b( array[a] > array[b]) thì phải đổi chỗ các phần tử. Có nhiều cách khác nhau để thay đổi địa điểm. Nhưng chúng ta sẽ sử dụng một kỹ thuật đơn giản, dễ hiểu và dễ nhớ:

private void swap(int[] array, int ind1, int ind2) {
    int tmp = array[ind1];
    array[ind1] = array[ind2];
    array[ind2] = tmp;
}
Bây giờ chúng ta có thể viết như sau:

int[] array = {10, 2, 10, 3, 1, 2, 5};
System.out.println(Arrays.toString(array));
for (int i = 1; i < array.length; i++) {
	if (array[i] < array[i - 1]) {	
		swap(array, i, i-1);
	}
}
System.out.println(Arrays.toString(array));
Như bạn có thể thấy, các yếu tố thực sự đã được hoán đổi. Chúng tôi đã bắt đầu với chỉ mục 1, bởi vì nếu mảng chỉ có một phần tử, thì biểu thức array[i] < array[i-1]không hợp lệ cho chỉ mục 0. Làm điều này cũng bảo vệ chúng ta khỏi các trường hợp mảng không có phần tử hoặc chỉ có một phần tử và nó làm cho mã trông đẹp hơn. Nhưng mảng kết quả vẫn chưa được sắp xếp, vì một lượt không đủ để thực hiện việc sắp xếp. Chúng ta sẽ phải thêm một vòng lặp khác trong đó chúng ta sẽ thực hiện lặp đi lặp lại cho đến khi nhận được một mảng đã sắp xếp:

int[] array = {10, 2, 10, 3, 1, 2, 5};
System.out.println(Arrays.toString(array));
boolean needIteration = true;
while (needIteration) {
	needIteration = false;
	for (int i = 1; i < array.length; i++) {
		if (array[i] < array[i - 1]) {
			swap(array, i, i-1);
			needIteration = true;
		}
	}
}
System.out.println(Arrays.toString(array));
Vì vậy, chúng tôi đã hoàn thành thuật toán sắp xếp đầu tiên của mình. Chúng tôi lặp lại vòng lặp bên ngoài ( while) cho đến khi chúng tôi quyết định rằng không cần lặp lại nữa. Theo mặc định, trước mỗi lần lặp mới, chúng tôi giả định rằng mảng của chúng tôi đã được sắp xếp và chúng tôi không cần lặp lại nữa. Theo đó, chúng tôi di chuyển tuần tự qua các yếu tố và kiểm tra giả định này. Nhưng nếu các phần tử không theo thứ tự, thì chúng tôi hoán đổi các phần tử và hiểu rằng bây giờ chúng tôi không đảm bảo rằng các phần tử theo đúng thứ tự. Điều này có nghĩa là chúng ta cần thực hiện một lần lặp khác. Ví dụ: giả sử chúng ta có [3, 5, 2]. 5còn hơn cả 3— tất cả đều tốt. Nhưng 2ít hơn 5. Tuy nhiên, [3, 2, 5]yêu cầu một lần vượt qua khác, vì3 > 2và chúng cần được hoán đổi. Bởi vì chúng tôi đang sử dụng một vòng lặp trong một vòng lặp, độ phức tạp của thuật toán của chúng tôi tăng lên. Các phần tử đã cho n, nó trở thành n * n, nghĩa là, O(n^2). Đây được gọi là độ phức tạp bậc hai. Nói chung, chúng ta không thể biết chính xác sẽ cần bao nhiêu lần lặp. Biểu thức độ phức tạp của một thuật toán cho thấy độ phức tạp tăng lên như thế nào trong trường hợp xấu nhất. Nghĩa là, nó cho biết thời gian thực hiện sẽ tăng lên bao nhiêu khi số phần tử nthay đổi. Sắp xếp bong bóng là một trong những thuật toán sắp xếp đơn giản và kém hiệu quả nhất. Đôi khi nó còn được gọi là "stupid sort". Tài liệu về chủ đề này:

sắp xếp lựa chọn

Một thuật toán sắp xếp khác là sắp xếp lựa chọn. Nó cũng có độ phức tạp bậc hai, nhưng nhiều hơn về điều đó sau. Vì vậy, ý tưởng là đơn giản. Trên mỗi lần vượt qua, chúng tôi chọn phần tử nhỏ nhất và chuyển nó về đầu. Ngoài ra, mỗi lần vượt qua bắt đầu một bước bên phải. Nói cách khác, lượt đầu tiên bắt đầu từ phần tử thứ 0, lượt thứ hai bắt đầu từ phần tử đầu tiên, v.v. Nó sẽ trông như thế này:

int[] array = {10, 2, 10, 3, 1, 2, 5};
System.out.println(Arrays.toString(array));
for (int left = 0; left < array.length; left++) {
	int minInd = left;
	for (int i = left; i < array.length; i++) {
		if (array[i] < array[minInd]) {
			minInd = i;
		}
	}
	swap(array, left, minInd);
}
System.out.println(Arrays.toString(array));
Cách sắp xếp này không ổn định, vì các phần tử giống hệt nhau (về bất kỳ đặc điểm nào mà chúng ta đang sử dụng để sắp xếp các phần tử) có thể thay đổi vị trí tương đối của chúng. Có một ví dụ điển hình là trong bài viết trên Wikipedia về sắp xếp lựa chọn . Tài liệu về chủ đề này:

Sắp xếp chèn

Sắp xếp chèn cũng có độ phức tạp bậc hai, vì chúng ta lại có một vòng lặp bên trong một vòng lặp. Điều gì làm cho sắp xếp chèn khác nhau? Thuật toán sắp xếp này là "ổn định." Điều này có nghĩa là các phần tử giống hệt nhau sẽ không thay đổi thứ tự tương đối của chúng. Một lần nữa, chúng tôi có nghĩa là giống hệt nhau về các đặc điểm mà chúng tôi sắp xếp theo.

int[] array = {10, 2, 10, 3, 1, 2, 5};
System.out.println(Arrays.toString(array));
for (int left = 0; left < array.length; left++) {
	// Get an element
	int value = array[left];
	// Iterate through the elements that are in front of this element
	int i = left - 1;
	for (; i >= 0; i--) {
		// If the current element is smaller, then move the larger element to the right.
		if (value < array[i]) {
			array[i + 1] = array[i];
		} else {
			// If the current element is larger, we stop
			break;
		}
	}
	// Insert the current value in the empty space
	array[i + 1] = value;
}
System.out.println(Arrays.toString(array));

loại xe đưa đón

Có một thuật toán sắp xếp đơn giản khác: sắp xếp con thoi. Điều này còn được gọi là sắp xếp bong bóng hai chiều hoặc sắp xếp bình lắc cocktail. Những tên thay thế này cho chúng ta biết rằng loại tàu con thoi không phải là về tàu con thoi. Đó là về một cái gì đó di chuyển qua lại. Bây giờ bạn có thể nghĩ về điều đó khi nghĩ về thuật toán này. Bản chất của thuật toán là gì? Bản chất của thuật toán là chúng ta lặp từ trái sang phải, hoán đổi các phần tử và kiểm tra xem có phần tử nào còn lại theo hướng khác cần hoán đổi hay không.

int[] array = {10, 2, 10, 3, 1, 2, 5};
System.out.println(Arrays.toString(array));
for (int i = 1; i < array.length; i++) {
	if (array[i] < array[i - 1]) {
		swap(array, i, i - 1);
		for (int z = i - 1; (z - 1) >= 0; z--) {
			if (array[z] < array[z - 1]) {
				swap(array, z, z - 1);
			} else {
				break;
			}
		}
	}
}
System.out.println(Arrays.toString(array));
Tài liệu về chủ đề này:

phân loại vỏ

Một thuật toán sắp xếp đơn giản khác là sắp xếp vỏ. Ý chính của nó tương tự như sắp xếp bong bóng, nhưng trong mỗi lần lặp lại, chúng ta có một khoảng cách khác nhau giữa các phần tử được so sánh. Nó được cắt làm đôi với mỗi lần lặp lại. Đây là một triển khai:

int[] array = {10, 2, 10, 3, 1, 2, 5};
System.out.println(Arrays.toString(array));
// Calculate the gap between the checked elements
int gap = array.length / 2;
// As long as there is a gap between the elements
while (gap >= 1) {
    for (int right = 0; right < array.length; right++) {
        // Shift the right index until we find one for which
        // there is the necessary gap between it and the element before it
       for (int c = right - gap; c >= 0; c -= gap) {
           if (array[c] > array[c + gap]) {
               swap(array, c, c + gap);
           }
        }
    }
    // Recalculate the gap
    gap = gap / 2;
}
System.out.println(Arrays.toString(array));
Tài liệu về chủ đề này:

Hợp nhất sắp xếp

Ngoài các thuật toán sắp xếp đơn giản này, còn có các thuật toán sắp xếp phức tạp hơn. Ví dụ: sắp xếp hợp nhất. Có hai điều cần lưu ý. Đầu tiên, đệ quy đến để giải cứu chúng tôi ở đây. Thứ hai, độ phức tạp của thuật toán không còn là bậc hai như chúng ta đã quen. Độ phức tạp của thuật toán này là logarit. Điều này được viết là O(n log n). Vì vậy, hãy thực hiện nó. Đầu tiên, chúng ta sẽ viết một lời gọi đệ quy tới phương thức sắp xếp:

public static void mergeSort(int[] source, int left, int right) {
        // Select the delimiter, i.e. split the input array in half
        int delimiter = left + ((right - left) / 2) + 1;
        // Recursively execute this function on the two halves (if we can split the input array)
        if (delimiter > 0 && right > (left + 1)) {
            mergeSort(source, left, delimiter - 1);
            mergeSort(source, delimiter, right);
        }
}
Bây giờ, hãy thêm hành động chính vào phần triển khai của chúng ta. Đây là siêu phương pháp của chúng tôi:

public static void mergeSort(int[] source, int left, int right) {
        // Select the delimiter, i.e. split the input array in half
        int delimiter = left + ((right - left) / 2) + 1;
        // Recursively execute this function on the two halves (if we can split the input array)
        if (delimiter > 0 && right > (left + 1)) {
            mergeSort(source, left, delimiter - 1);
            mergeSort(source, delimiter, right);
        }
        // Create a temporary array with the required size
        int[] buffer = new int[right - left + 1];
        // Starting from the specified left index, go through each element.
        int cursor = left;
        for (int i = 0; i < buffer.length; i++) {
            // We use delimeter to point to the element on the right half
            // If delimeter> right, then the right half has no elements that haven't been added
            if (delimiter > right || source[cursor] > source[delimiter]) {
                buffer[i] = source[cursor];
                cursor++;
            } else {
                buffer[i] = source[delimiter];
                delimiter++;
            }
        }
        System.arraycopy(buffer, 0, source, left, buffer.length);
}
Chúng ta có thể chạy ví dụ của mình bằng cách gọimergeSort(array, 0, array.length-1). Như bạn có thể thấy, quy trình rút gọn lại để chấp nhận một mảng đầu vào cùng với các chỉ dẫn về phần đầu và phần cuối của phần cần được sắp xếp. Khi sắp xếp bắt đầu, đây là phần đầu và phần cuối của mảng. Sau đó, chúng tôi tính toán dấu phân cách, là chỉ mục mà chúng tôi sẽ phân chia mảng. Nếu mảng có thể được chia thành 2 phần, thì chúng ta gọi phương thức sắp xếp theo cách đệ quy cho hai nửa của mảng. Chúng tôi chuẩn bị một bộ đệm phụ, nơi chúng tôi sẽ chèn phần đã sắp xếp. Tiếp theo, đặt chỉ mục ở đầu phần sẽ được sắp xếp và bắt đầu đi qua từng phần tử của bộ đệm trống, lấp đầy nó bằng các phần tử nhỏ nhất. Nếu phần tử được trỏ đến bởi chỉ mục nhỏ hơn phần tử được trỏ đến bởi dấu phân cách, chúng ta đặt phần tử vào bộ đệm và dịch chuyển chỉ mục. Nếu không thì, chúng tôi đặt phần tử được trỏ bởi dấu phân cách vào bộ đệm và dịch chuyển dấu phân cách. Ngay khi dấu phân cách vượt ra ngoài ranh giới của phần được sắp xếp hoặc chúng tôi điền vào toàn bộ mảng, phạm vi đã chỉ định được coi là đã sắp xếp.Tài liệu về chủ đề này:

Đếm sắp xếp và sắp xếp cơ số

Một thuật toán sắp xếp thú vị khác là sắp xếp đếm. Độ phức tạp của thuật toán ở đây là O(n+k), ở đâu nlà số phần tử và klà giá trị lớn nhất của một phần tử. Thuật toán này có một thiếu sót: chúng ta cần biết các giá trị tối thiểu và tối đa trong mảng. Dưới đây là một ví dụ về sắp xếp đếm:

public static int[] countingSort(int[] theArray, int maxValue) {
        // An array of "counters", ranging from 0 to the maximum value
        int numCounts[] = new int[maxValue + 1];
        // We increase the counter in the corresponding cell (index = value)
        for (int num : theArray) {
            numCounts[num]++;
        }
        // Create an array to hold the sorted result
        int[] sortedArray = new int[theArray.length];
        int currentSortedIndex = 0;
        // Run through the array of "counters"
        for (int n = 0; n < numCounts.length; n++) {
            int count = numCounts[n];
            // Run through the number of values
            for (int k = 0; k < count; k++) {
                sortedArray[currentSortedIndex] = n;
                currentSortedIndex++;
            }
        }
        return sortedArray;
    }
Bạn có thể hiểu rằng rất bất tiện khi chúng ta cần biết trước giá trị nhỏ nhất và giá trị lớn nhất. Và chúng ta có một thuật toán khác ở đây: sắp xếp cơ số. Tôi sẽ chỉ trình bày thuật toán ở đây một cách trực quan. Xem các tài liệu bổ sung để thực hiện: Tài liệu:

Sắp xếp nhanh chóng

Chà, đã đến lúc ăn tráng miệng — sắp xếp nhanh, một trong những thuật toán sắp xếp nổi tiếng nhất. Nó có độ phức tạp logarit: O(n log n). Thuật toán sắp xếp này được phát triển bởi Tony Hoare. Thật thú vị, anh ấy đã phát minh ra nó khi sống ở Liên Xô, nơi anh ấy học dịch máy tại Đại học Moscow và phát triển một cuốn sách cụm từ tiếng Nga-Anh. Hơn nữa, Java sử dụng phiên bản phức tạp hơn của thuật toán này trong Arrays.sort. Thế còn Collections.sort? Tại sao không xem xét cách mọi thứ được sắp xếp "dưới mui xe"? Đây là mã:

public static void quickSort(int[] source, int leftBorder, int rightBorder) {
        int leftMarker = leftBorder;
        int rightMarker = rightBorder;
        int pivot = source[(leftMarker + rightMarker) / 2];
        do {
            // Move the left marker from left to right as long as the element is less than pivot
            while (source[leftMarker] < pivot) {
                leftMarker++;
            }
            // Move the right marker as long as the element is greater than pivot
            while (source[rightMarker] > pivot) {
                rightMarker--;
            }
            // Check whether we need to swap the elements pointed to by the markers
            if (leftMarker <= rightMarker) {
                // The left marker will be less than the right one only if we need to do a swap
                if (leftMarker < rightMarker) {
                    int tmp = source[leftMarker];
                    source[leftMarker] = source[rightMarker];
                    source[rightMarker] = tmp;
                }
                // Shift the markers to get new borders
                leftMarker++;
                rightMarker--;
            }
        } while (leftMarker <= rightMarker);

        // Execute recursively on the parts
        if (leftMarker < rightBorder) {
            quickSort(source, leftMarker, rightBorder);
        }
        if (leftBorder < rightMarker) {
            quickSort(source, leftBorder, rightMarker);
        }
}
Đây là tất cả những thứ rất đáng sợ, vì vậy hãy tìm hiểu sâu về nó. Đối với mảng đầu vào ( int[]nguồn ), chúng ta tạo hai điểm đánh dấu: trái ( L) và phải ( R). Trong lần gọi phương thức đầu tiên, chúng tương ứng với phần đầu và phần cuối của mảng. Sau đó, chúng tôi xác định phần tử trục, được đặt tên một cách thích hợp pivot. Sau đó, nhiệm vụ của chúng ta là di chuyển các giá trị nhỏ hơn pivotsang trái của pivot, và các giá trị lớn hơn — sang phải. Để làm điều này, trước tiên chúng tôi di chuyển Lđiểm đánh dấu cho đến khi chúng tôi tìm thấy giá trị lớn hơn pivot. Nếu không tìm được giá trị nào nhỏ hơn thì L trở thành bằng pivot. Sau đó, chúng tôi di chuyển Rđiểm đánh dấu cho đến khi chúng tôi tìm thấy một giá trị nhỏ hơn pivot. Nếu không tìm thấy giá trị lớn hơn, thì Rtrở thành bằng pivot. Tiếp theo, nếu Lđiểm đánh dấu nằm trước Rđiểm đánh dấu hoặc trùng với nó, thì chúng ta thử đổi chỗ các phần tử nếu phần Ltử nhỏ hơn Rphần tử. Sau đó, chúng tôi dịch chuyển Lsang phải 1 và chúng tôi dịch chuyển Rsang trái 1. Khi Lđiểm đánh dấu di chuyển ra ngoài Rđiểm đánh dấu, điều đó có nghĩa là quá trình hoán đổi đã hoàn tất: giá trị nhỏ hơn ở bên trái của pivot, giá trị lớn hơn ở bên phải của pivot. Sau đó, chúng tôi gọi đệ quy cùng một phương thức sắp xếp trên các mảng con — từ đầu phần được sắp xếp đến điểm đánh dấu bên phải và từ điểm đánh dấu bên trái đến cuối phần được sắp xếp. Tại sao từ đầu đến điểm đánh dấu bên phải? Bởi vì khi kết thúc một lần lặp, điểm đánh dấu bên phải di chuyển đủ để trở thành ranh giới của phần bên trái. Thuật toán này phức tạp hơn so với cách sắp xếp đơn giản, vì vậy tốt nhất bạn nên phác thảo nó. Lấy một tờ giấy trắng và viết: 4 2 6 7 3. Sau đó viết pivotvào giữa, tức là dưới số 6. Khoanh tròn nó. Dưới 4, viết Lvà dưới 3, viết R. 4 nhỏ hơn 6, 2 nhỏ hơn 6. Cuối cùng chúng ta di chuyển Lđến pivotvị trí, vì Lkhông thể di chuyển qua pivot, theo điều kiện. Viết lại 4 2 6 7 3. Khoanh tròn 6 ( pivot) và đặt Lbên dưới nó. Bây giờ di chuyển điểm Rđánh dấu. 3 nhỏ hơn 6, vì vậy hãy đặt Rđiểm đánh dấu là 3. Vì 3 nhỏ hơn 6 ( pivot), nên chúng tôi thực hiện phép tính swap. Viết kết quả: 4 2 3 7 6. Khoanh vào số 6, vì nó vẫn là pivot. Điểm Lđánh dấu ở trên 3. Điểm Rđánh dấu ở trên 6. Hãy nhớ rằng chúng ta di chuyển các điểm đánh dấu cho đến khi Lvượt ra ngoài R. Di chuyển Lđến số tiếp theo. Ở đây tôi muốn khám phá hai khả năng: 1) trường hợp số áp chót là 7 và 2) trường hợp số đó là 1 chứ không phải 7. Nếu số áp chót là 1: Di chuyển Lđiểm đánh dấu đến 1, vì chúng ta có thể di chuyển Lmiễn là Lđiểm đánh dấu đến một số nhỏ hơn pivot. Nhưng chúng ta không thể di chuyển Rkhỏi 6, bởi vì chúng ta chỉ có thể di chuyển R nếu Rđiểm đánh dấu trỏ đến một số lớn hơn pivot. Chúng tôi không thực hiện a swap, vì 1 nhỏ hơn 6. Viết tình huống hiện tại: 4 2 3 1 6. Khoanh tròn 6 ( pivot). Lchuyển động pivotvà ngừng chuyển động. Rkhông di chuyển. Chúng tôi không thực hiện hoán đổi. Dịch chuyển LRtheo một vị trí. Viết Rđiểm đánh dấu dưới 1. Điểm Lđánh dấu nằm ngoài giới hạn. Vì Llà ngoài giới hạn, chúng tôi không làm gì cả. Nhưng, chúng ta viết lại 4 2 3 1, vì đây là vế trái của chúng ta, nhỏ hơn pivot(6). Chọn cái mới pivotvà bắt đầu lại mọi thứ :) Nếu số áp chót là 7:Di chuyển Lđiểm đánh dấu đến 7. Chúng tôi không thể di chuyển điểm đánh dấu bên phải vì nó đã trỏ vào trục. 7 lớn hơn pivot, vì vậy chúng tôi thực hiện a swap. Viết kết quả: 4 2 3 6 7. Khoanh tròn số 6, vì nó là pivot. Điểm Lđánh dấu hiện được chuyển thành 7 và Rđiểm đánh dấu được chuyển thành 3. Sẽ không có ý nghĩa gì khi sắp xếp phần từ Lđầu đến cuối vì chỉ có 1 phần tử. Tuy nhiên, chúng tôi gửi phần từ 4 đến điểm Rđánh dấu để phân loại. Chúng tôi chọn a pivotvà bắt đầu lại từ đầu :) Thoạt nhìn, có vẻ như nếu bạn thêm nhiều giá trị bằng pivot, thì bạn sẽ phá vỡ thuật toán. Nhưng đây không phải là trường hợp. Bạn có thể nghĩ ra các kết hợp phức tạp và trên giấy thuyết phục bản thân rằng mọi thứ đều đúng và ngạc nhiên rằng những hành động đơn giản như vậy lại thực hiện một cơ chế phân loại đáng tin cậy như vậy. Nhược điểm duy nhất là thuật toán sắp xếp này không ổn định. Bởi vì một sự hoán đổi có thể thay đổi thứ tự tương đối của các phần tử giống hệt nhau nếu một trong số chúng gặp phải trước pivotkhi phần tử kia được hoán đổi thành phần trước đó pivot.

Điểm mấu chốt

Ở trên, chúng ta đã xem xét một bộ thuật toán sắp xếp của "quý ông" được triển khai trong Java. Các thuật toán nói chung là hữu ích, cả từ góc độ ứng dụng và về mặt học cách suy nghĩ. Một số đơn giản hơn, một số phức tạp hơn. Những người thông minh đã bảo vệ luận án của họ cho một số người. Đối với những người khác, họ đã viết những cuốn sách siêu dày. Tôi hy vọng các tài liệu bổ sung sẽ giúp bạn tìm hiểu nhiều hơn nữa, vì bài viết này chỉ là một cái nhìn tổng quan đã trở nên có trọng lượng. Và mục đích của nó là để cung cấp một giới thiệu nhỏ. Bạn cũng có thể tìm thấy phần giới thiệu về thuật toán nếu bạn đọc "Thuật toán Grokking ". Tôi cũng thích danh sách phát từ Jack Brown: Quyết định AQA 1 1.01 Truy tìm thuật toán . Để giải trí, bạn có thể xem trực quan thuật toán lúc sắp xếp. trực quango.net .
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION