"선택 정렬"을 사용한 알고리즘 프로그램 작성 철학, 시간 및 공간 복잡도 계산, 알고리즘의 정확성 검증 방법에 대해 토론합니다.

정렬 알고리즘 - 선택 정렬

평가:
가장 단순함: 사람들의 자연스러운 사고와 가장 일치함.
가장 유용하지 않음: 기본적으로 엔지니어링 실무에 사용되지 않음

시간 복잡도: O(n^2)
공간 복잡도: O(1)

이 섹션의 내용:
시간 및 공간 복잡도 계산 방법
알고리즘 검증 방법 - 난수 생성기, 로그
알고리즘 프로그램 작성 철학

선택 정렬 알고리즘 아이디어

인간의 생각과 가장 일치하는 알고리즘으로
순회할 때마다 가장 작은 값을 찾아 모든 값을 찾을 때까지 맨 앞에 놓는(스왑) 방식이다. 순서가 정렬되어 있습니다. 작은 것부터 큰 것까지
여기서 "삽입" 대신 "교환"이 필요한 이유는 무엇입니까? 개인적인 이해: 메모리 배열에서 배열 요소의 "교환"은 더 작은 메모리 영역을 이동하는 반면 "삽입"은 더 많은 메모리를 필요로 합니다. 지역이 이동되었습니다

알고리즘 프로그램을 작성하는 방법은 무엇입니까? 알고리즘 작성 철학

  • 단순한 것부터 복잡한 것까지
    • 단계별로 확인
    • 여러 중간 결과 인쇄
  • 부분이 먼저이고 그다음 전체
    • 잘 모르겠으면 먼저 분해해 보세요.
  • 처음엔 거칠다가 나중에 괜찮아
    • 변수 이름 바꾸기
    • 명세서 병합
    • 경계처리

선택 정렬 알고리즘과 관련하여 작성 방법은 무엇입니까?

단순한 것에서 복잡한 것까지: 이중 레이어 루프를 먼저 무시하고, 가장 안쪽 루프만 작성하고, 첫 번째 핵심 작업만 수행합니다: 루프는 배열에서 최소값의 첨자를 찾습니다. 더 가까운 분할: 구체적으로 변수가 있습니다
. 최소값이 있는 첨자 먼저 첨자가 0이라고 가정하고 1의 위치부터 선택을 시작합니다. 특정 요소에 도달했을 때 비교 결과 첨자가 0인 값보다 작은 값이 있는 것으로 확인되면 업데이트합니다. 최소값입니다. 값 아래 첨자 변수의 값은 새로운 아래 첨자입니다.

대략적인 코드 버전:

private static void sort1(int[] arr) {
    
    
    int minPos = 0;
    for (int i = 1; i < arr.length; i++) {
    
    
        if (arr[minPos] > arr[i]) {
    
    
            minPos = i;
        }
    }
    // 此时下标为 minPos 的值就是我们挑选出来的最小值
    System.out.println("minPos = " + minPos);
    System.out.println("arr = " + Arrays.toString(arr));
    // 交换: 将最小值与下标为0的值做交换
    int tmp = arr[0];
    arr[0] = arr[minPos];
    arr[minPos] = tmp;
    // 再次打印
    System.out.println("after arr1 = " + Arrays.toString(arr));

    // 第一轮执行完毕,准备第二轮
    minPos = 1;
    for (int i = 2; i < arr.length; i++) {
    
    
        if (arr[minPos] > arr[i]) {
    
    
            minPos = i;
        }
    }
    // 此时下标为 minPos 的值就是挑选出来的最小值
    System.out.println("minPos = " + minPos);
    System.out.println("arr = " + Arrays.toString(arr));
    // 交换: 将最小值与下标为1的值做交换
    tmp = arr[1];
    arr[1] = arr[minPos];
    arr[minPos] = tmp;
    // 交换后,下标为1的位置就是本轮挑选出来的最小值
    System.out.println("arr2 = " + Arrays.toString(arr));

    // ...
    // 发现规律:每一轮的结果就是挑选出一个最小值,并与指定下标的值交换
    // 待交换的下标是从0开始递增,直到length - 1
}

유선

규칙 살펴보기: 각 라운드의 결과는 최소값을 선택하고 이를 지정된 첨자의 값과 교환하는 것입니다. 교환할 첨자는
0부터 시작하여 길이 - 1까지 증가합니다.

private static void sort2(int[] arr) {
    
    
    for (int i = 0; i < arr.length - 1; i++) {
    
    
        int minPos = i;
        for (int j = i + 1; j < arr.length; j++) {
    
    
            if (arr[minPos] > arr[j]) {
    
    
                minPos = j;
            }
        }
        System.out.println("第 " + i + " 轮外层循环,找到的最小值下标: " + minPos);
        int tmp = arr[i];
        arr[i] = arr[minPos];
        arr[minPos] = tmp;
        System.out.println("交换后的数组 = " + Arrays.toString(arr));
    }
    System.out.println("arr = " + Arrays.toString(arr));
}

시간 및 공간 복잡도 계산

시간 복잡도를 먼저 분석한 후 공간 복잡도를 분석하세요.

시간복잡도와 공간복잡도를 계산하기 위해 sort2 방식을 예로 들어 각 문장의 시간복잡도를 분석하는데, 표현의 편의를 위해 자바코드의 문장을 시간복잡도 단위로 표현한다.

시간 복잡도 분석

private static void sort2(int[] arr) {
    
     // 方法名,不计入复杂度,假设参数重的arr长度为n,以下的代码将以n为基础
    for (int i = 0; // 代码 i=0 只会执行一次,计为1
    		i < arr.length - 1; // 代码 i<arr.length-1 将执行 length-1 次,计为n-1,又因n很大的时候与n-1相差无几,所以可直接计为n
    		i++) {
    
     // 代码 i++ 将执行n次,计为n
        int minPos = i; // 执行 n-1 次,计为 n
        for (int j = i + 1; // 该语句将执行 (n-1) 次,与外层循环的次数一致
        	j < arr.length; // 该语句将执行 (n-1)+(n-2)+(n-3)+...+2+1 次,这是个等差数列,换算一下为: n(n-1)/2 = n^2/2 - n/2
        	j++) {
    
     // 该语句是上句话的次数+1,同理当n很大的时候,与n相关的+1相差无几,所以为了方便计算,+1就不在单独加一,即该语句与上句话的时间复杂度相同
            if (arr[minPos] > arr[j]) {
    
     // 该语句与等差数列的次数相同
                minPos = j; // 同上
            }
        }
        System.out.println("第 " + i + " 轮外层循环,找到的最小值下标: " + minPos); // 该语句是调试打印语句,不计入时间复杂度
        int tmp = arr[i]; 		// 该语句将执行 (n-1) 次
        arr[i] = arr[minPos]; 	// 同上
        arr[minPos] = tmp; 		// 同上
        System.out.println("交换后的数组 = " + Arrays.toString(arr)); 			// 调试打印语句,忽略
    }
    System.out.println("arr = " + Arrays.toString(arr)); 						// 调试打印语句,忽略
}

위 소스코드 주석의 시간복잡도 분석에서 파워가 가장 높은 시간복잡도가 연산수열, 즉 n^2/2 - n/2전체 알고리즘의 시간복잡도는 전체 알고리즘으로서 파워가 가장 높은 문장만 찾으면 된다는 것을 알 수 있다. 시간 복잡도는 충분합니다.
그런 다음 복잡한 공식을 단순화하고 상수 항을 무시하고 저전력 항을 무시하고 가장 높은 전력만 유지합니다. 값은 , n^2즉 전체 알고리즘의 시간 복잡도는 다음과 같습니다.O(n^2)

2레벨 루프의 시간 복잡도는 임을 알 수 있으며 n^2, 미래에는 다중 레벨 루프를 직접 검색할 수 있으며, 기본 레벨 루프의 기본 시간 복잡도는 n의 여러 거듭제곱으로 올라갑니다.

공간 복잡도 분석

空间复杂度알고리즘에 필요한 추가 공간을 나타냅니다. 원리: 데이터 크기와 관련된 공간을 주로 찾고, for 루프의 인덱스 변수 , 내부 루프의 인덱스 변수
등 개별 기본 유형 변수를 무시하고, 교환에 사용되는 변수 선택 정렬 알고리즘 도 무시합니다. , 추가로 사용되지 않는 데이터량에 해당하는 공간이므로 공간복잡도가 가장 기본ijint tmp
O(1)

선택 정렬의 "불안정성"

불안정성이란 무엇입니까? 즉, 배열에 등가값을 갖는 두 개의 요소가 존재하는데, 선택 정렬 후 두 요소를 교환할 수 있다. 이 두 요소의 상대적 위치가 불안정합니다.

예를 들어 다음이 정렬할 배열이라고 가정합니다.
int[] arr = {5, 4, 2, 5, 1, 3};
메서드 sort2()에서 교환이 발생하기 전에 디버깅 인쇄 코드를 추가합니다 .
System.out.printf("即将发生交换arr[%s]=%s <-> arr[%s]=%s\n", i, arr[i], minPos, arr[minPos]);

다음 출력을 볼 수 있습니다.

即将发生交换arr[0]=5 <-> arr[4]=1
即将发生交换arr[1]=4 <-> arr[2]=2
即将发生交换arr[2]=4 <-> arr[5]=3
即将发生交换arr[3]=5 <-> arr[5]=4
即将发生交换arr[4]=5 <-> arr[4]=5
arr = [1, 2, 3, 4, 5, 5]

결과의 마지막 행에서는 정렬 결과가 실제로 작은 것부터 큰 순서로 정렬되어 있음을 알 수 있습니다. 이것은 올바른 순서이지만 걱정하지 마십시오. 값이 5인 요소가 0 에 첨자가 붙은
값 5의 교환 경로: [0] -> [4]
첨자가 3인 값 5의 교환 경로: [3] -> [5]
어떤 사람들은 두 값의 상대적인 순서가 ​​​여기서 5는 변경되지 않았습니다. 첫 번째 5는 위치 0에서 위치 4로 교환되고 두 번째 5는 위치 3에서 위치 5로 교환됩니다. 교환 후에도 여전히 상대적 순서를 유지합니다.
걱정하지 마십시오. 위의 예는 제가 임의로 작성한 것이므로 특별한 불안정 장면에 부딪치지 않았습니다. 다음으로 이 예를 사용하여 원본 배열의 순서를 약간 변경하여 불안정한 장면을 트리거하겠습니다. 마지막 두 숫자를 변경합니다
. 1과 3이 교환되어 다음 배열을 얻습니다.
int[] arr = {5, 4, 2, 5, 3, 1};
출력을 얻기 위해 선택 정렬 알고리즘을 다시 실행합니다.

即将发生交换arr[0]=5 <-> arr[5]=1
即将发生交换arr[1]=4 <-> arr[2]=2
即将发生交换arr[2]=4 <-> arr[4]=3
即将发生交换arr[3]=5 <-> arr[4]=4
即将发生交换arr[4]=5 <-> arr[4]=5
arr = [1, 2, 3, 4, 5, 5]

값 5와 인덱스 0의 교환 경로: [0] -> [5]
값 5와 인덱스 3의 교환 경로: [3] -> [4]
이때 처음 5는 에서 교환됩니다. 위치 0 위치 5번에서는 두 번째 5가 위치 3에서 위치 4로 바뀌었으므로 두 5의 상대적 위치가 각각 교환된 후 변경되고 불안정 장면이 트리거되었습니다.

불안정한 분류가 실제 비즈니스에 미치는 영향의 예

은행 예금자는 입금액 기준으로 대소 순으로 정렬되어 있는데, 같은 금액의 예금자가 많아서 등액의 예금자의 순서가 불안정한 경우가 있습니다. 혹시 특정 이벤트에서 상위 1,000명에게만 선물을 보내는 경우도 있습니다. 불안정한 정렬로 인해 N번째 사람에게 할당된 금액은 50만명이고 정확히 50만명의 예금자는 100명입니다.100명을 모두 선물 범위에 포함시키면 총 인원은 1,001명이 되지만 비즈니스 규칙 선착순 1,000명만 포함할 수 있도록 엄격히 요구하므로, 예치금 500,000원을 입금한 예금자가 있어야 제외됩니다.
선택 정렬 알고리즘은 정렬 전 예금이 500,000인 예금자의 상대적 순서가 변경되지 않도록 보장할 수 없으며 이것이 선택 정렬 알고리즘의 "불안정성"입니다.

알고리즘을 확인하는 방법은 무엇입니까?

검증 알고리즘 – Logarithmizer(어레이 비교를 위한 도구)

  1. 시각적 관찰 – 엔지니어링 코드가 아닌 테스트 코드에만 사용됩니다. 이유는 오류가 발생하기 쉽고 샘플이 크면 육안으로 볼 수 없기 때문입니다. 따라서 단계는 항목 2부터 시작됩니다.
  2. 충분한 무작위 샘플 생성
  3. 올바른 알고리즘을 사용하여 샘플 결과 계산
  4. 검증된 알고리즘의 결과 비교

충분한 무작위 샘플 생성

배열을 생성하고, 매개변수로 숫자를 지정하고, 임의의 객체를 생성한 후, 배열의 각 첨자에 대해 임의의 정수를 생성합니다. 임의의 정수의 최대값은 매개변수로 지정됩니다. 코드는 다음과 같습니다.

private int[] generateRandomArray(int len, int max) {
    
    
    int[] arr = new int[len];
    Random r = new Random();
    for (int i = 0; i < arr.length; i++) {
    
    
        arr[i] = r.nextInt(max);
    }
    return arr;
}

두 배열이 같은지 비교

길이가 동일하지 않으면 배열이 동일하지 않아야 합니다. 그렇지 않으면 두 배열을 순회하여 두 배열의 해당 첨자에 있는 값을 가져옵니다. 비교를 위해 동일하지 않으면 동일하지 않음이 반환됩니다. 순회 끝에서 동일하지 않은 항목이 발견되지 않으면 반환은 동일하며 코드는 다음과 같습니다.

private boolean checkArrayEquals(int[] arr1, int[] arr2) {
    
    
    if (arr1.length != arr2.length) {
    
    
        return false;
    }

    for (int i = 0; i < arr1.length; i++) {
    
    
        if (arr1[i] != arr2[i]) {
    
    
            return false;
        }
    }
    return true;
}

대규모 배열의 첫 번째 N 요소에 대한 보조 인쇄

큰 배열에 값이 너무 많아서 다 출력하기가 불편하고, 정렬 시나리오에서는 처음 N개의 요소가 순서대로 있는지 확인하는 것만으로 전체 배열의 순서를 대략적으로 반영할 수 있다. 다음과 같습니다:

private void printFirstN(int[] arr, int n) {
    
    
    System.out.print("first-" + n + ": ");
    for (int i = 0; i < n; i++) {
    
    
        System.out.print(arr[i] + " ");
    }
    System.out.println();
}

검증 알고리즘의 주요 기능

이전에 보조 기능을 완료한 후 주 기능을 작성하면 됩니다. 먼저 10,000개의 요소로 구성된 배열을 무작위로 생성한 다음 하나를 복사하여 두 개의 복사본을 얻습니다. 하나는 자체 정렬 알고리즘을 사용하여 정렬하는 데 사용되고 다른 하나는 표준 정렬 기능을 사용하여 정렬하는 데 사용됩니다. 그런 다음 두 정렬 결과를 비교합니다. 알고리즘., 코드는 아래와 같이 표시됩니다.

public void test_data_checker() {
    
    
    for (int i = 0; i < 10; i++) {
    
    
        int[] arr = generateRandomArray(10000, 10000);
        int[] arr2 = new int[arr.length];
        System.arraycopy(arr, 0, arr2, 0, arr.length);
        printFirstN(arr, 10);
        SelectionSort.sort2(arr);
        Arrays.sort(arr2);
        printFirstN(arr, 10);
        printFirstN(arr2, 10);
        boolean b = checkArrayEquals(arr, arr2);
        if (!b) {
    
    
            throw new RuntimeException("排序结果与标准排序算法不一致");
        }
    }
}

위의 로직을 루프로 감싸서 여러 번 반복하고, 한 번에 결과가 다른 경우 예외 메시지가 발생합니다.

추천

출처blog.csdn.net/booynal/article/details/125590320