1. 程式人生 > >LeetCode 31. Next Permutation 找到一個數組的下一個更大的字典序排序

LeetCode 31. Next Permutation 找到一個數組的下一個更大的字典序排序

Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.

If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).

The replacement must be  and use only constant extra memory.

Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.

1,2,3 → 1,3,23,2,1 → 1,2,31,1,5 → 1,5,1

方法一:根據字典序規律來程式設計

找到從前往後最大序號的一個元素(假設序號為j),從後往前找值和序號均比它大的元素(假設序號為i),將這兩個元素交換後,還要將序號為j之後的所有元素升序排序。

這樣找得到的序列是下一個更大的字典序排序是因為i是從前往後找其後還有比它自身值更大的元素的最大序號,這樣當從後往前找時在i之後不會再存在一個元素,其後還有元素比它自身的值更大。這樣的結果就是nums[j]已經是i之後所有元素中最小的比nums[i]大的元素了。簡而言之,就是找到一個元素,在該元素之後還有元素比它值更大,且該元素已經是滿足前面條件的最大序號元素,然後將該元素與其後比它大的元素中值最小的元素互換位置,最後將交換之後序號在i之後的所有元素按升序排序即可。

class Solution {
public:
	//找到從前往後最大序號的一個元素(假設序號為j),從後往前找值和序號均比它大的元素(假設序號為i),將這兩個元素交換後,還要將序號為j之後的所有元素升序排序
	//這樣獲得的才是next greater permutation of numbers.
	void nextPermutation(vector<int>& nums) {
		int len = nums.size();
		if (len <= 1)
			return;

		bool isgreatest = true;
		int record_left = 0, record_right = 0; //record_left,record_right要是能換的最大序號,
		for (int i = len - 1; i > 0; --i) {
			for (int j = i - 1; j >= 0; --j) {
				if (nums[j] < nums[i]) {
					if (j > record_left || (j == record_left && i >= record_right)) {
						record_left = j;
						record_right = i;
						isgreatest = false;
					}
				}
			}
		}

		if (isgreatest) {
			//如果已經是最大字典序了,則返回最小字典序
			reverse(nums);
		}
		else {
			swap(nums[record_left], nums[record_right]);
			sort(nums, record_left+1);
		}
	}

	void swap(int &a, int&b) {
		int temp = a;
		a = b;
		b = temp;
	}

	void sort(vector<int> &nums, int begin) {
		if (begin >= nums.size() - 1)
			return;

		int tmp = 0;
		int lastExchangeIndex = 0;//記錄最後一次交換的位置
		int sortBorder = nums.size() - 1;//無序陣列的邊界,每次比較只需要比較到這裡為止
		for (int i = begin; i < nums.size(); ++i) {
			//有序標記,每一輪的初始是true
			bool issorted = true;
			for (int j = begin; j < sortBorder; ++j) {
				if (nums[j] > nums[j + 1]) {
					//有元素交換,所以不是有序,標記為false
					issorted = false;
					swap(nums[j], nums[j + 1]);
					//把無序數列的邊界更新為最後一次交換元素的位置
					lastExchangeIndex = j;
				}
			}
			sortBorder = lastExchangeIndex;
			if (issorted)
				break; //如果已經是有序的了,直接結束迴圈,不需要繼續後面的比較了
		}
	}

	void reverse(vector<int> &nums) {
		int left = 0, right = nums.size() - 1;
		while (left <= right) {
			swap(nums[left], nums[right]);
			++left;
			--right;
		}
	}
};

Complexity Analysis

  • Time complexity : O(n). In worst case, only two scans of the whole array are needed.

  • Space complexity : O(1). No extra space is used. In place replacements are done.

方法二:與方法一原理相同,換一種實現方式

  1. Start from its last element, traverse backward to find the first one with index i that satisfy num[i-1] < num[i]. So, elements from num[i] to num[n-1] is reversely sorted.
  2. To find the next permutation, we have to swap some numbers at different positions, to minimize the increased amount, we have to make the highest changed position as high as possible. Notice that index larger than or equal to i is not possible as num[i,n-1] is reversely sorted. So, we want to increase the number at index i-1, clearly, swap it with the smallest number between num[i,n-1] that is larger than num[i-1]. For example, original number is 121543321, we want to swap the '1' at position 2 with '2' at position 7.
  3. The last step is to make the remaining higher position part as small as possible, we just have to reversely sort the num[i,n-1]

JAVA程式碼實現如下:

public class Solution {
    public void nextPermutation(int[] nums) {
        int i = nums.length - 2;
        while (i >= 0 && nums[i + 1] <= nums[i]) {
            i--;
        }
        if (i >= 0) {
            int j = nums.length - 1;
            while (j >= 0 && nums[j] <= nums[i]) {
                j--;
            }
            swap(nums, i, j);
        }
        reverse(nums, i + 1);
    }

    private void reverse(int[] nums, int start) {
        int i = start, j = nums.length - 1;
        while (i < j) {
            swap(nums, i, j);
            i++;
            j--;
        }
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

C++程式碼如下:

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
    	int n = nums.size(), k, l;
    	for (k = n - 2; k >= 0; k--) {
            if (nums[k] < nums[k + 1]) {
                break;
            }
        }
    	if (k < 0) {
    	    reverse(nums.begin(), nums.end());
    	} else {
    	    for (l = n - 1; l > k; l--) {
                if (nums[l] > nums[k]) {
                    break;
                }
            } 
    	    swap(nums[k], nums[l]);
    	    reverse(nums.begin() + k + 1, nums.end());
        }
    }
}; 

方法三:直接使用C++庫函式

Solution 1

Just for info: There's a library function that does the job, even going from totally reverse sorted to sorted:

void nextPermutation(vector<int>& nums) {
    next_permutation(begin(nums), end(nums));
}

Solution 2

Using library functions for all building blocks of the algorithm. Very nice how they all play together, notice the total lack of +1/-1, it all fits exactly.

void nextPermutation(vector<int>& nums) {
    auto i = is_sorted_until(nums.rbegin(), nums.rend());
    if (i != nums.rend())
        swap(*i, *upper_bound(nums.rbegin(), i, *i));
    reverse(nums.rbegin(), i);
}

Solution 3

Doing it all on my own (except swap, let's not be silly):

void nextPermutation(vector<int>& nums) {
    int i = nums.size() - 1, k = i;
    while (i > 0 && nums[i-1] >= nums[i])
        i--;
    for (int j=i; j<k; j++, k--)
        swap(nums[j], nums[k]);
    if (i > 0) {
        k = i--;
        while (nums[k] <= nums[i])
            k++;
        swap(nums[i], nums[k]);
    }
}

Solution 4

Ok, let's be silly after all and not even use swap :-)

void nextPermutation(vector<int>& nums) {
    int i = nums.size() - 1, k = i, tmp;
    while (i > 0 && nums[i-1] >= nums[i])
        i--;
    for (int j=i; j<k; j++, k--)
        tmp = nums[j], nums[j] = nums[k], nums[k] = tmp;
    if (i > 0) {
        k = i--;
        while (nums[k] <= nums[i])
            k++;
        tmp = nums[i], nums[i] = nums[k], nums[k] = tmp;
    }
}