2016-02-28 16 views
5

Tôi đang học Java ở trường ngay bây giờ và chủ đề mới nhất của chúng tôi là sắp xếp các thuật toán trong Java. Cái mà tôi đang cố gắng hiểu là quicksort.Java Quicksort tại sao/giá trị thay đổi ở đâu?

Để hiểu cách thuật toán đó sắp xếp các số trong một mảng, tôi quyết định thực hiện bước mã của mình cho bước trong cửa sổ trình gỡ lỗi Eclipse.

Bây giờ có một bước mà tôi không thể hiểu được ngay cả sau khi trải qua những gì cảm thấy như hàng trăm lần.

mảng ban đầu của tôi là [10, 5, 3, 22, 11, 2]

Khi tôi đi qua các mã chương trình bắt đầu bằng cách trao đổi 102, sau đó 53 và sau đó 22. Tại thời điểm đó, giá trị cho i1 và giá trị cho j-1.

Bây giờ cách tôi nhìn thấy nó là có ba điều kiện

  1. while(i<=j) nào trả false, vì i = 1j = -1

  2. if(left < j) nào trả false, vì left = 0j = -1

  3. if(i < right) Mà còn trả false, vì i = 1right = 1

Nhưng tôi ngạc nhiên khi chương trình được cho khung cuối cùng ngay trước khi public static void display chương trình bỏ qua trở lại dòng 40 if(i < right) nhưng đột nhiên các giá trị cho right, i, jpivot đã thay đổi từ lần lượt là 5, 2, -13.

Tôi sẽ là rất biết ơn nếu ai đó có thể giải thích lý do tại sao các giá trị được thay đổi.

Tôi cũng đã bổ sung thêm hai hình ảnh mà hiển thị những gì tôi nhìn thấy trên cửa sổ Eclipse của tôi step I don't understand

public class QSort { 

    public static void quickSort(int[] arr, int left, int right){ 
     int i = left; 
     int j = right; 
     int temp; 
     int pivot = arr[(left+right)/2]; 
     System.out.println("\n\nleft = " + left + "\tright = " + right); 
     System.out.println("Pivot is: " + pivot + "(" + (left+right)/2 + ")"); 
     while(i <= j){ 
      while(arr[i] < pivot){ 
       System.out.println("i is: " + arr[i] + "(" + i + ")"); 
       i++; 
       System.out.println("i is: " + arr[i] + "(" + i + ")"); 
      } 
      while(arr[j] > pivot){ 
       System.out.println("j is: "+ arr[j] + "(" + j + ")"); 
       j--; 
       System.out.println("j is: "+ arr[j] + "(" + j + ")"); 
      } 
      if(i <= j){ 
       System.out.println("i is: " + arr[i] + "(" + i + ")"); 
       System.out.println("j is: "+ arr[j] + "(" + j + ")"); 
       System.out.println("Swapped " + arr[i] + "(" + i + ")"+ " with " + arr[j] + "(" + j + ")"); 
       temp = arr[i]; 
       arr[i] = arr[j]; 
       arr[j] = temp; 
       i++; 
       j--; 
       System.out.println("i is: (" + i + ")"); 
       System.out.println("j is: (" + j + ")"); 
       System.out.println("Pivot is: " + pivot + "(" + (left+right)/2 + ")"); 
      } 
     } 
     if(left < j){ 
      System.out.println("j is: (" + j + ")"); 
      quickSort(arr, left, j); 
     } 
     if(i < right){ 
      System.out.println("i is: (" + i + ")"); 
      quickSort(arr, i, right); 
     } 
    } 

    public static void display(int[] arr){ 
     if(arr.length > 0){ 
      System.out.print(arr[0]); 
     } 
     for(int i = 1; i < arr.length; i++){ 
      System.out.print(", " + arr[i]); 
     } 
    } 

    public static void main(String[] args) { 
     int[] data = new int[]{10,5,3,22,11,2}; 
     System.out.println("Before: "); 
     display(data); 
     quickSort(data, 0, data.length-1); 
     System.out.println("\nAfter: "); 
     display(data); 
    } 
} 

Cảm ơn rất nhiều!

+0

Bạn có thể muốn có một sneek @ [Java Thực hiện sắp xếp nhanh] (http://codereview.stackexchange.com/questions/4022/java-implementation-of-quick-sort) – Abhijeet

Trả lời

3

Tôi nghĩ rằng vấn đề của bạn là bạn không hoàn toàn hiểu được đệ quy. Ít nhất đó là những gì nó âm thanh như từ desription của bạn của câu hỏi.

Dù sao, tôi đã cố gắng đơn giản theo dõi chương trình của bạn bằng cách theo dõi tất cả các biến. Hy vọng điều này sẽ giúp:

arr     left  right  i   j  pivot 
----------------- ------- -------- ------- ----- ---------- 
[10,5,3,22,11,2] 0   5   
              0   5  arr[2] = 3 
[2,5,3,22,11,10]       1   4 
                3 
                2 
[2,3,5,22,11,10]       2   1 

Vòng lặp while đã kết thúc vì i<=j tại là false (2 > 1).

Điều kiện đầu tiên left < j (0 < 1) là true, vì vậy bạn gọi quicksort một lần nữa đệ quy: quicksort(arr, 0, 1) - có nghĩa là bây giờ bạn sắp xếp các mảng [2,3] đó đã được sắp xếp, vì vậy không có gì sẽ xảy ra:

arr     left  right  i   j  pivot 
----------------- ------- -------- ------- ----- ---------- 
[2,3,5,22,11,10] 0   1 
              0   1  arr[0] = 2 
                0 
[2,3,5,22,11,10]       1   -1 

Các trong khi điều kiện vòng lặp hiện là false. Điều kiện đầu tiên left < jfalse là tốt (vì 0 > -1) và điều kiện thứ hai i < right cũng sai (vì 1 == 1) Vì vậy, cuộc gọi này kết thúc và bạn quay lại vị trí hiện tại của bạn. Đó là? Điều kiện đầu tiên của bảng trên. Trạng thái của biến và thông số bây giờ là:

arr     left  right  i   j  pivot 
----------------- ------- -------- ------- ----- ---------- 
[10,5,3,22,11,2] 0   5   2   1  3 

Điều kiện thứ hai được chọn (vì nó là dòng tiếp theo được thực hiện). Giá trị của nó cũng đúng vì điều kiện là i < right (2 < 5). Vì vậy, bây giờ bạn làm quicksort một lần nữa đệ quy: quicksort(arr, 2, 5) - có nghĩa là bây giờ bạn sắp xếp các mảng [3,22,11,10]:

arr     left  right  i   j  pivot 
----------------- ------- -------- ------- ----- ---------- 
[2,3,5,22,11,10]   2   5   
              2   5  arr[3] = 22 
              3 
[2,3,5,10,11,22]       4   4 
              5 

i > j ngay bây giờ để chúng ta thoát khỏi vòng lặp while.

Điều kiện đầu tiên left < j (2 < 4) là true, vì vậy chúng ta gọi là quicksort(arr, 2, 4) để sắp xếp [5,10,11] đó đã được sắp xếp. Tôi sẽ bỏ qua phần này vì nó không thay đổi mảng nào cả.

Khi cuộc gọi đệ quy được thực hiện, chúng tôi trở lại vị trí hiện tại của chúng tôi và hiện tại điều kiện thứ hai sẽ được kiểm tra. i < right (5 < 5) là false Và vì vậy chúng tôi đã hoàn tất.

Cuộc gọi quicksort ban đầu đã kết thúc và mảng được sắp xếp.

+0

Cảm ơn bạn rất nhiều vì những nỗ lực của bạn, điều này làm cho nó rất dễ hiểu cho tôi. Và bạn đã đúng, tôi đã không hiểu làm thế nào đệ quy hoạt động. Tôi nghĩ khi chúng ta gọi một quicksort, quicksort "chính" dừng lại và được thực hiện làm việc. =) –

2

Hình ảnh đầu tiên bạn đã hiển thị trình gỡ lỗi nằm bên trong hai lời gọi đệ quy của quicksort: quicksort được gọi từ chính, và sau đó trên dòng 38 gọi quicksort một lần nữa (đây là nguyên tắc của quicksort, đó là một chiến lược đệ quy). Vì vậy, bạn thấy bạn đang trên đường 40 của cuộc gọi bên trong, sau đó khi bạn bước từ đó, bạn quay trở lại lời gọi trước đó của quicksort (trình gỡ lỗi hiển thị cho bạn một chồng hai dòng thay vì ba trong cửa sổ trên cùng bên trái), và bạn quay trở lại các giá trị trước đó của trục, vv Mảng được truyền như tất cả các lời gọi đệ quy sao cho nó được chia sẻ, nhưng các biến số nguyên thì không.

Tôi nghĩ rằng đây là đệ quy khiến bạn khó hiểu, hãy kiểm tra ngăn xếp cuộc gọi của bạn.

+0

Cảm ơn sự giúp đỡ của bạn! Đây cũng là lần đầu tiên tôi sử dụng trình gỡ lỗi nhật thực và tôi không hoàn toàn chắc chắn về tất cả những điều này có ý nghĩa gì. Không biết chương trình đã nhảy trở lại cuộc gọi trước đó của quicksort :) Cảm ơn bạn. –

1

Những nỗ lực của bạn trong việc nghiên cứu thuật toán sắp xếp bằng cách sử dụng các câu lệnh in và trình gỡ lỗi là đáng khen ngợi! Nhưng việc triển khai Quicksort hiện tại của bạn khá khó hiểu, ít nhất là ban đầu, bởi vì nó có cả lặp lại và đệ quy đang diễn ra (tức là bạn sử dụng vòng lặp và đồng thời, bạn có một thủ tục tự gọi là quicksort).

Hãy thực hiện một cách tiếp cận khá khác (cách tiếp cận hoàn toàn đệ quy) để xem cách hoạt động của Quicksort và lý do hoạt động. Chuyển đổi thủ tục đệ quy này thành một thủ tục lặp đi lặp lại (giống như bạn đã viết), thật may mắn, một vấn đề kỹ thuật (trong trường hợp này)! Tôi đề nghị chúng tôi làm điều này ở đây vì cách đó chúng tôi có thể kiểm soát sự phức tạp tốt hơn. Một lần nữa, lý do tôi làm điều này là để hiểu rõ hơn những gì đang xảy ra.

Khi Sir Tony Hoare đề xuất các thuật toán và chứng minh tính đúng đắn của nó, nó là một cái gì đó như thế này:

public static void qsort(int[] ints, int fi, int li) {       
    /* the recursive procedure */             
    if (fi < li) {                 
     int p = partition(ints, fi, li); // key routine -- see below           
     qsort(ints, fi, p - 1);              
     qsort(ints, p + 1, li);              
    }                    
    } 

Vậy là xong! Không đẹp sao? Vâng, nó là. Tất cả những gì bạn làm là:

  • Phân vùng mảng đã cho. Trong mắt tôi, phân vùng là một quy trình thanh lịch để giải quyết vấn đề phức tạp. Vấn đề rất đơn giản: Cho một mảng số, sắp xếp lại mảng sao cho có một số trong đó, tất cả các số ở bên trái nhỏ hơn hoặc bằng số và tất cả các số ở bên phải lớn hơn lớn hơn hoặc bằng nó - trả về chỉ mục của một phần tử như vậy trong mảng. Tôi khuyên bạn cố gắng giải quyết vấn đề này một mình. Cả hai thủ tục (Lomuto và Hoare) được đưa ra trên Wikipedia hoạt động tốt.
  • Khi bạn chắc chắn rằng phân vùng của bạn hoạt động như mong đợi, bạn đệ quy loại hai phân vùng bằng cách sử dụng thủ tục tương tự qsort (thứ tự của các cuộc gọi đệ quy không không vấn đề) và bạn đã làm xong!

tôi đã cố gắng để thực hiện các thủ tục partition bản thân mình:

/** 
    Implements partitioning of array a from a[p] to a[r], leaves all the other elements of the array intact. 
    @param a the given int array 
    @param p int first index of the part of array to be partitioned 
    @param r int the second index of the part of array to be partitioned 
*/ 
    public static int partition(int[] a, int p, int r) { 
    //this is something I have written 
    int pivot = a[p]; 
    int i = p + 1; 
    int j = i - 1; 
    while (i <= r) { 
     if (a[i] < pivot) { 
     a[j] = a[i]; 
     a[i] = a[j+1]; 
     j++; 
     } 
     i++; 
    } 
    a[j] = pivot; 
    return j; 
    } 

    private static void swap(int[] ints, int i1, int i2) { 
    int tmp = ints[i2]; 
    ints[i2] = ints[i1]; 
    ints[i1] = tmp; 
    } 

Bây giờ, tôi vẫn chưa chứng minh sự đúng đắn của thủ tục này, nhưng chúng ta có thể làm điều đó một cách riêng biệt. Bạn thậm chí có thể reimplement các Lomuto procedure và thấy rằng nó hoạt động đến sự hài lòng của bạn.

Và đó là nó. Nếu bây giờ bạn muốn gỡ lỗi này bằng trình gỡ lỗi, bạn sẽ được trang bị nhiều hơn để làm điều đó. Đây là entire implementation.

Bây giờ, câu hỏi về quá trình chuyển đổi đệ quy này để là một lặp đi lặp lại là rất thú vị và tài liệu này: (không biết xấu hổ cắm: Tôi đã viết nó) http://bit.ly/recurse-iterate nên cung cấp cho bạn một số manh mối. Việc chuyển đổi thực tế của thủ tục Quicksort ở trên thành một thủ tục lặp lại được để lại như một bài tập cho người đọc :-).

+0

Tôi phải thừa nhận rằng tôi không tự viết tất cả mã đó ngoại trừ các câu lệnh in, tôi đã sử dụng nó làm ví dụ để hiểu cách Quicksort hoạt động trong Java. Cảm ơn bạn đã cung cấp thông tin và đề xuất bổ sung của bạn Tôi chắc chắn sẽ kiểm tra nó. Cảm ơn :) –

Các vấn đề liên quan