**1. Starting with "insert Sort"**

**Insert Sort (insert-sort)** One of the applications in life is when playing poker, the right hand (regardless of left-handed) constantly picks up a poker from the table and inserts it into the left-hand poker sequence at the appropriate position. At the end of the game, the left hand poker is orderly.

The algorithm is very simple, the following directly gives the Java implementation code:

/*** Insert Sort * *@paraman array that needs to be sorted*/ Public Static voidInsertsort (int[] a) { for(inti = 1; i < a.length; i++) { intj = I-1; //Curr represents the poker to be inserted intCurr =A[i]; //to insert the poker from the left hand to the right of the poker sequence and keep moving to the left//stop moving until the end or the poker you want to insert is bigger than a poker in the left hand while(J >-1 && Curr <A[j]) {A[j+ 1] =A[j]; J--; } a[j+ 1] =Curr; } }

**2. Theoretical proof**

In the above program, the end of each cycle (outer for loop), A[0~i] (for the 0~i bit of a array) is well-ordered, we put A[0~i] This property form as a **cyclic** invariant.

**cyclic invariant** can be used to help us understand the correctness of an algorithm:

It is true before the loop is first iterated;

It is also true before the next iteration if it is true before the iteration of a loop.

At the end of the loop, the invariant gives us a useful property, which helps to prove that the algorithm is correct;

Use the above theory to verify that the above algorithm is correct:

- Initialization: Before the first iteration, the sub-array has only one a[0], which is clearly established;
- Hold: Assuming that the invariant is established before the iteration of the first I, that is, the sub-array a[0~i-1] is ordered, and, in the first iteration, according to the algorithm, A[i] moves to the left from the far right of the sub-array a[0~i-1] until it finds its proper position, when the subarray is expanded to a by a[0~i-1] [0~i]. Therefore, the sub-array A[0~i] is also well-sequenced before the i+1 iteration.
- Termination: The condition of the outer for loop termination is, I = a.length-1. Depending on the
**retention** , the subarray is expanded to a[0~a.length-1], and is well-sequenced.

So the algorithm is correct.

**3. Algorithm Analysis**

We assume a generic single-processor computational model-the Random Access machine (random-access Machine,ram)-as a model for implementing technology. In this model, some commonly used computer instructions, including arithmetic instructions (add, subtract, multiply, divide, withdraw, rounding up, rounding down), data movement instructions (loading, storage, copying) and control instructions (conditional and unconditional transfer, subroutine call and return), they are required to execute the time is constant.

Also, take the above **insert sort (insert-sort)** example to illustrate:

//(c, N), the first parameter represents the time that the step was executed, and the second parameter represents the number of times that the step was executed. for(inti = 1; i < a.length; i++) {//(c1, N) intj = i-1;//(C2, n-1) intCurr = A[i];//(C3, N-1) while(J >-1 && curr < a[j]) {//(C4, t2+t3+...+tn)A[j + 1] = A[j];//(C5, (t2-1) + (t3-1) +...+ (tn-1))j--;//(C6, (t2-1) + (t3-1) +...+ (tn-1))} a[j+ 1] = Curr;//(C7, n-1)}

Calculate Total time:

**Best case:** T (n) =c1 * n + c2 * (n-1) + C3 * (n-1) + C4 * (n-1) + c7 * (n-1) = (C1 + C2 + c3 + C4 + C7) * N – (C2 + c3 + C4 + C7)

We represent T (n) as **an+b**, which is a linear function of N.

**Worst case scenario:** T (n) = C1 * n + (C2 + c3 + C7) * (n-1) + C4 * [N (1 + N)/2-1] + (c5 + c6) * [N (1 + N)/2] = (C4/2 + C5/2 + c6/ 2) * n²+ (c1 + C2 + c3 + C7-c4/2-C5/2-C6/2) * N – (C2 + c3 + C7 + C4)

We represent T (n) as **an^2 + bn + C**, which is a two-time function of N.

To do a more streamlined abstraction, what we really want to be interested in is the **growth rate** of the computation time (the **scale of growth** ). Therefore, the constant coefficients of the lower order and the highest order are ignored. The best case for **insert sort (insert-sort)** run time is: Θ (n), and the worst-case run time is θ (n²).

For the **insertion sort (insert-sort)**, we should consider the worst case scenario, because: ① the worst case gives an upper bound, to ensure that the algorithm does not exceed a certain time, ② worst case often occurs, ③ "average" and worst case is roughly the same poor.

**4. Algorithm design**

**Insert Sort (insert-sort)** takes the **increment** method, inserts A[i] into the sub-array a[0~i-1], and the subarray grows to: a[0~i].

The following is a design method called divide-and- **conquer (Divide and Conquer)** : to **decompose** the original problem into smaller but similar sub-problems, to **solve** these sub-problems recursively, and then **merge **the solution of these sub-problems to establish the solution of the original problem.

The **merge sort (merge-sort)** follows the divide-and- **Conquer method (Divide and Conquer)**, with the following steps:

**decomposition** divides the sequence of n elements to be sorted into 2 sub-sequences with N/2 elements;
**Resolve** 2 sub-sequences by using merge sort recursion recursively;
**Merge** Merge sorted 2 sub-sequences get answers;

The Java implementation code for **merge sort (merge-sort)** is given below:

/*** Sort A[p~r] * *@paramA *@paramp *@paramR*/ Public Static voidMergeSort (int[] A,intPintr) {if(P <r) {intQ = (r + P)/2; MergeSort (A, p, q); MergeSort (A, Q+ 1, R); Merge (A, p, Q, R); } } /*** Merge 2 sorted sequences (a[p ~ Q] and a[q+1 ~ R]) * *@paramA *@paramp q >= p *@paramQ *@paramR*/ Public Static voidMergeint[] A,intPintQintr) {int[] A1 =New int[Q-p + 2]; int[] A2 =New int[R-q + 1]; for(inti = 0; i < a1.length-1; i++) {A1[i]= A[p +i]; } a1[a1.length-1] =Integer.max_value; for(inti = 0; i < a2.length-1; i++) {A2[i]= A[q + i + 1]; } a2[a2.length-1] =Integer.max_value; intm = 0, n = 0; for(inti = P; I < R + 1; i++) { if(A1[m] <A2[n]) {A[i]=A1[m]; M++; } Else{A[i]=A2[n]; N++; } } }

The following analytical **division method (Divide and Conquer)**:

Set T (n) as the time required to apply the divide-and-conquer method to the problem of size n. If the problem size is small enough, such as for a constant c,n <= C, then the direct solution requires constant time, recorded as: θ (1); otherwise, the problem is decomposed into a sub-problem, the size of each sub-problem is the original 1/b, then T (n) = A * T (n/b), if decomposed into sub-problem time is D Can be recorded as: Θ (1)), the time required to merge sub-problems is C (n) (can be remembered as: Θ (n)), then the T (N) recursion is:

In particular, for **merge sort (merge-sort)**,

can be calculated when n->∞, T (n) = c * N * LG N + CN, recorded as: Θ (n * lg N).

Visible when n is large, in the worst case, the **merge sort (merge-sort)** is better than the **insertion sort (insert-sort)**.

**PS:** all of these are excerpted from the Chinese translation of the introduction to algorithms. I just extracted the article in the personal opinion of the more important points, added some personal understanding, for reference only. Some of the sentences and words are translated, so they may be more abrupt and better.

1. Algorithm Basics