Data structure (17)-commonly used algorithms (1)

Preface

  In the previous articles, we introduced you to the data structure, including the recommendation of data tools , top ten sorting algorithms , overview of data structures and algorithms , linked lists , stacks , queues , sorting and search algorithms, and trees and graphs . From the beginning of this article, I will introduce you to the knowledge of algorithms, mainly including binary search, divide and conquer algorithm, dynamic programming, KMP algorithm, greedy algorithm, prim algorithm, kruskal algorithm, Dijkstra algorithm, Floyd algorithm, and horse board algorithm. First, I will introduce a relatively simple binary search algorithm.

One, binary search algorithm

  In fact, we introduced binary search in the previous search . The implementation method we introduced at that time was carried out by recursion. This time, the binary search is implemented in a non-recursive way. Binary search is only suitable for searching from an ordered sequence of numbers, and searching after sorting the sequence of numbers. The time complexity of O(logn)binary search is that it only takes one log2_nstep at most to find the desired target position . Assuming that the search target from the queue of [0,99] is 30, log2_100 is required, that is, 7 searches are required at most. Next, we take the array {1,3,8,10,11,67,100}as an example to implement the binary search algorithm. The specific code implementation is as follows:

public class BinarySearchNoRecur {
    
    
    public static void main(String[] args) {
    
    
        int[] arr = {
    
    1,3,8,10,11,67,100};
        int index = binarySearch(arr,100);
        System.out.println ("index="+index);
    }
    public static int binarySearch(int[] arr, int target){
    
    
        int left = 0;
        int right = arr.length - 1;
        while (left <= right){
    
    
            int mid = left + (right - left) / 2;
            if(arr[mid] == target){
    
    
                return mid;
            }
            else if (arr[mid] > target){
    
    
                return mid - 1;
            }
            else {
    
    
                left = mid + 1;
            }
        }
        return -1;
    }
}

  Let's take the search for 100 as an example, the execution result is as follows:

2. Divide and conquer

  Divide and conquer is an important algorithm. In fact 分而治之, it means: divide a complex problem into two or more identical and similar sub-problems, and then divide the sub-problems into smaller sub-problems... until the final sub-problems can be solved simply and directly. The solution of the problem is the combination of the solutions of the sub-problems. In fact, this algorithm is the basis of many algorithms we will introduce later, so we need to focus on it. We generally use the classic scenarios of divide and conquer: 二分搜索、大整数乘法、棋盘覆盖、合并排序、快速排序、线性时间选择、最接近点对问题、循环赛日程表以及汉诺塔wait. Next, I will introduce the basic steps of the divide and conquer method.
  In fact, the divide-and-conquer method has three steps at each level of recursion:

  • 1. Decomposition : Decompose the original problem into several smaller, independent sub-problems with the same form as the original problem.
  • 2. Solve : if the sub-problems are small and easy to be solved, solve them directly, otherwise solve each sub-problem recursively.
  • 3. Combine : Combine the solutions of each sub-problem into the solution of the original problem.

  The pseudo code of the divide and conquer algorithm is as follows:

if |p| <= n0 		
	then return (ADHOC(p))
	//将p分解为较小的子问题p1、p2,……,pk
	for i<——1 to k
	do yi<——Divide-and-Conquer(Pi) //递归解决pi
	T <——MERGE(y1,y2,……,yk)//合并子问题
	return T

  In the above pseudo-code, where |p| represents the scale of the problem p; n0 is a threshold, which means that when the scale of the problem p does not exceed n0, the problem can be solved directly without further decomposition. ADHOC( p) is the basic sub-algorithm in the divide-and-conquer method, used to directly solve small-scale problems p. Therefore, when the scale of p does not exceed n0, it is directly solved by the algorithm ADHOC(P). The algorithm MERGE(y1,y2,……,yk) is the merge sub-algorithm in the divide and conquer method, used to merge the corresponding solutions y1,y2,……yk of the subproblems p1,p2,……,pk The solution to p.

One of the typical applications of divide and conquer-Tower of Hanoi

  The problem with the Tower of Hanoi originated from an ancient legendary educational toy in India. When the Brahma created the world, he made three diamond pillars. On one of the pillars, he held 64 golden discs in order of size from bottom to top. The Brahma ordered the Brahman to reposition the discs from the bottom in order of size. On a pillar. It is also stipulated that the disc cannot be enlarged on the small disc, and only one disc can be moved between the three pillars at a time.

  If it takes one second for each disk to move, how long does it take to move all the disks? A scholar’s ​​research found that it takes more than 584.554 billion years to move these disks, and the life expectancy of the solar system is said to be tens of billions of years. We use the game of the Tower of Hanoi to carry out a simple analysis of ideas:

  If we have a disk, A->C,
  if we have n>=2, we can always regard the two disks as the bottom disk of the two disks.
  The top disk is the top disk. The top disk A->B is
  the top disk. The lower disk A->C
  changes all the disks of Tower B from B->C

  According to the above analysis of the idea of ​​the Tower of Hanoi, we use java to realize it:

public class Hanoitower {
    
    
    public static void main(String[] args) {
    
    
        hanoiTower(10, 'A', 'B', 'C');
    }
    public static void hanoiTower(int num, char a, char b, char c) {
    
    
        //如果只有一个盘
        if(num == 1) {
    
    
            System.out.println("第1个盘从 " + a + "->" + c);
        } else {
    
    
            //如果我们有 n >= 2 情况,我们总是可以看做是两个盘 1.最下边的一个盘 2. 上面的所有盘
            //1. 先把 最上面的所有盘 A->B, 移动过程会使用到 c
            hanoiTower(num - 1, a, c, b);
            //2. 把最下边的盘 A->C
            System.out.println("第" + num + "个盘从 " + a + "->" + c);
            //3. 把B塔的所有盘 从 B->C , 移动过程使用到 a塔
            hanoiTower(num - 1, b, a, c);
        }
    }
}

  The specific implementation results are as follows:

Three, dynamic programming algorithm

  The core of dynamic programming is actually to divide a big problem into several small problems, so that the optimal solution will be obtained step by step. In fact, dynamic programming is similar to the divide-and-conquer algorithm we introduced earlier. Its basic idea is to decompose the sub-problems to be solved into several sub-problems, first solve the sub-problems, and then obtain the solution of the original problem from the solutions of these sub-problems. But the difference with the divide-and-conquer method is that it is suitable for problems solved by dynamic programming, and the sub-problems obtained by decomposition are often not independent of each other. The other is that dynamic programming can be advanced step by step by filling in the form, so as to obtain the optimal solution.

Classic application of dynamic programming algorithm-knapsack problem

  There is a backpack with a capacity of 4 pounds. The following items

  are available: The requirements are as follows:

  • 1. The achieved goal is the maximum total value of the loaded backpack, and the repetition does not exceed a fixed number
  • 2. The items required to be loaded cannot be repeated

  The specific ideas are as follows:

  Backpacks mainly refer to a backpack with a given capacity and a number of items with a certain value and weight. How to choose items to put in the backpack to maximize the value of the items, which is divided into 01 backpacks and complete backpacks (here, the complete backpack is for each item There are unlimited pieces of use )
  Of course, we mentioned the 01 backpack problem, that is, each item can put at most one. Of course, the infinite knapsack problem can also be converted to the 01 knapsack problem.
  The main idea of ​​the algorithm is: use dynamic programming to solve. That is: each time the ith item is reached, w[i] and v[i] are used to determine whether the item needs to be put into the backpack. We can set v[i] and w[i] as the value and weight of the i-th item in the given n items, and C as the capacity of the backpack. Let v[i][j] denote the maximum value that can be loaded into the backpack of capacity j in the first i-th item.

  Specifically, we use diagrams to illustrate the above ideas. The process of backpacking is as follows:

  1. If there is only a guitar (G) at this time, no matter how large the backpack is, you can only put a guitar 1500 (G)
  2. If there are guitars and speakers, verify the formula:
v[1][1] =1500

  1. i = 1, j = 1
  2. w[i] = w[1] = 1 w [1] = 1 j = 1 v[i][j]=max{v[i-1][j], v[i]+v[i-1][jw[i]]} : v[1][1] = max {v[0][1], v[1] + v[0][1-1]} = max{0, 1500 + 0} = 1500 v[3][4]
  3. i = 3;j = 4 w[i] = w[3] =3 j = 4 j = 4 >= w[i] = 3 => 4 >= 3
    v[3][4] = max {v[2][4], v[3] + v[2][1]} = max{3000,2000+1500} = 2000+1500

  Specifically, we use code to implement the above ideas:

public class KnapsackProblem {
    
    
    public static void main(String[] args) {
    
    
        int[] w = {
    
    1, 4, 3};//物品的重量
        int[] val = {
    
    1500, 3000, 2000}; //物品的价值 这里val[i] 就是前面讲的v[i]
        int m = 4; //背包的容量
        int n = val.length; //物品的个数
        int[][] v = new int[n+1][m+1];
        
        int[][] path = new int[n+1][m+1];
      
        for(int i = 0; i < v.length; i++) {
    
    
            v[i][0] = 0; //将第一列设置为0
        }
        for(int i=0; i < v[0].length; i++) {
    
    
            v[0][i] = 0; //将第一行设置0
        }
        for(int i = 1; i < v.length; i++) {
    
     
            for(int j=1; j < v[0].length; j++) {
    
    
                if(w[i-1]> j) {
    
     
                    v[i][j]=v[i-1][j];
                } else {
    
    
                    if(v[i - 1][j] < val[i - 1] + v[i - 1][j - w[i - 1]]) {
    
    
                        v[i][j] = val[i - 1] + v[i - 1][j - w[i - 1]];
                        //把当前的情况记录到path
                        path[i][j] = 1;
                    } else {
    
    
                        v[i][j] = v[i - 1][j];
                    }

                }
            }
        }
        for(int i =0; i < v.length;i++) {
    
    
            for(int j = 0; j < v[i].length;j++) {
    
    
                System.out.print(v[i][j] + " ");
            }
            System.out.println();
        }

        System.out.println("============================");
        int i = path.length - 1; //行的最大下标
        int j = path[0].length - 1;  //列的最大下标
        while(i > 0 && j > 0 ) {
    
     //从path的最后开始找
            if(path[i][j] == 1) {
    
    
                System.out.printf("第%d个商品放入到背包\n", i);
                j -= w[i-1]; //w[i-1]
            }
            i--;
        }
    }
}

  The result of the execution is shown in the figure below:

to sum up

  What we introduced earlier is the data structure, which mainly includes the recommendation of data tools , the top ten sorting algorithms , an overview of data structures and algorithms , linked lists , stacks , queues , sorting and search algorithms, and trees and graphs . Starting from this article, I will introduce you to the knowledge of algorithms. This article mainly introduces three commonly used algorithms, including binary search, divide and conquer, and dynamic programming. For the latter two, we give classic applications to help you better Understand the core idea of ​​its algorithm. In fact, data structures and algorithms are particularly important and play a vital role in programming. Therefore, we need special mastery. Life is endless and struggle is endless. We work hard every day, study hard, constantly improve our abilities, and believe that we will learn something. Come on! ! !

Guess you like

Origin blog.csdn.net/Oliverfly1/article/details/113929428