Introduction to Algorithms: Algorithmic Diagram

Posted by php_joe on Tue, 30 Jul 2019 16:48:42 +0200

Introduction to Algorithms: Algorithmic Diagram

Introduce an introductory book on the basis of algorithms. It is necessary to supplement algorithms and data structures for non-academic students, but these things are often so dull that many people's enthusiasm is dispelled. Algorithmic Diagram uses python as programming language, and for some basic algorithms. The introduction can be said to be very easy to understand, really suitable for entry, at the same time, I also combined some school enrollment topics to expand to a certain extent, some use C++.

Binary Search (O(log n))

def binary_search(list,item):
	low = 0;
	high = len(list)-1
	while low <= high :
		mid = int((low+high)/2)
		guess =list[mid]
		if guess == item:
			return mid
		if guess < item:
			low = mid+1
		else:
			high=mid-1
	return None
my_list =[1,3,5,7,9]
print (binary_search(my_list,5))
print (binary_search(my_list,-1))

Output results:

2
None

Binary lookup has many examinations, such as the one-year byte-jumping programming problem which uses this method:

Problem Description:

There are n ropes with different lengths, which can be cut arbitrarily and can not be spliced together. It is required to get m ropes with equal lengths after cutting. The maximum length L of these ropes with equal lengths is obtained by inquiring.

Input: Number of ropes n; Length of n ropes; Number of ropes to be cut m
Output: Maximum length of m ropes cut into the same length

Idea analysis:

This problem is actually a dichotomy search problem, which is different from dynamic programming (in the next section I will talk about is another problem of cutting rope), because we know the longest rope, we can determine that the length of the final cut can only be in the middle of 0 to this number, so we do a dichotomy search on this range. After that, we construct a function to judge whether the guess value meets the requirements. This function is to traverse all the ropes and calculate how many segments of ropes of this length can be cut out. If the number is larger than m, it will be shorter. If the number is smaller than m, it will be longer. This dichotomy search is quite clear.

N = input()
N=int (N)
max_len = 0.0
min_len = 0.0
num={}
for i in range(N):
	num[i]=int (input())
	if num[i]>max_len:
		max_len=num[i]
M = input()
M=int (M)
def check(length):
	ans = 0
	for i in range(N):
		ans += (int)(num[i]/length)
	return ans
while max_len-min_len>=0.00001:
	mid = (max_len+min_len)/2
	if(M>check(mid)):
		max_len=mid
	else:
		min_len=mid
print(mid)

Example results:

5
5
5
8
10
10
45
0.8333301544189453

This question card is about accuracy. Access can only be achieved by improving the accuracy.

Sorting algorithm

The common sorting algorithms introduced in this book are Selective Sorting and Quick Sorting. The following is the procedure of Selective Sorting:

def findsmallest(arr):
	smallest = arr[0]
	smallest_index = 0
	for i in range(0,len(arr)):
		if arr[i]<smallest:
			smallest = arr[i]
			smallest_index=i
		return smallest_index
def selectionsort(arr):
	newArr=[0]*7
	for i in range(len(arr)):
		smallest=findsmallest(arr)
		newArr[i]=(arr.pop(smallest))
	return newArr
print(selectionsort([5,3,6,2,10,22,45]))

Example results:

[5, 3, 6, 2, 10, 22, 45]

According to the content of the book, after introducing the selection and sorting, I talked about recursion. I believe that many people have a certain understanding of the return. The introduction of the book is based on the implementation of stack principle. If you are interested in it, you can go to see it. I will go directly to the quick sorting. I will implement it through a topic on Niuke.com:

Problem Description:

In order to find a satisfactory job, Niuniu collected the difficulty and remuneration of each job. The criterion of cattle selection is that when the difficulty does not exceed its ability value, cattle choose the work with the highest pay. After Niu Niu has chosen his own job, Niu Niu's companions come to Niu Niu to help him choose his job. Niu Niu still uses its own standards to help his companions. Niuniu has too many small companions, so he has to give you the task.

Input:

Each input contains a test case. The first line of each test case contains two positive integers, representing the number of jobs N (N<=100000) and the number of partners M (M<=100000), respectively. The next N lines contain two positive integers, representing the difficulty of the work Di (Di <= 1000000000) and the reward Pi (Pi <= 1000000000). The next line contains M positive integers, representing the capability value Ai (Ai <= 1000000000) of the M companion, respectively. Guarantee that no two jobs are paid the same

Output:

For each partner, output a positive integer on a separate line to indicate the maximum reward he can get. A job can be chosen by more than one person.

The code is as follows:
#include<iostream>
#include<algorithm>
using namespace std;
bool cmp(int a[] , int b[])
{
	if(a[1]==b[1])
	{
		return a[0]>b[0];
	}
	return a[1]>b[1];
}
//use the quicksort algorithm to solve the two dimension array
void quicksort(int **a,int left,int right)
{
	int pos1=left;
	int pos2=right;
	if(left<right)
	{
		int temp=a[left][0];
		int temp1=a[left][1];
		while(left<right)
		{
			while(left<right&&(a[right][0]>temp||(a[right][0]==temp&&a[right][1]>temp1)))
				right--;
			a[left][0]=a[right][0];
			a[left][1]=a[right][1];
			while(left<right&&(a[left][0]<temp||(a[left][0]==temp&&a[left][1]<temp1)))
				left++;
			a[right][0]=a[left][0];
			a[right][1]=a[left][1];
		}
		a[left][0]=temp;
		a[left][1]=temp1;
		quicksort(a,pos1,left-1);
		quicksort(a,left+1,pos2);
	}


}
int main()
{
	int num_of_work,num_of_worker;
	cin>>num_of_work>>num_of_worker;
	int **work=new int*[num_of_work];
	for(int i=0;i<num_of_work;i++)
	{
		work[i]=new int[2];
		cin>>work[i][0]>>work[i][1];
	}
	//sort the array by the algorithm in C++,and we get the ascent order
	sort(work,work+num_of_work,cmp);
	//sort the array by the self function,and we get the descent order
	//quicksort(work,0,num_of_work-1);

	int *worker=new int[num_of_worker];
	int *money=new int[num_of_worker];
	for(int i=0;i<num_of_worker;i++)
	{
		cin>>worker[i];
		int cando=worker[i];
		for(int j=0;j<num_of_work;j++)
		{
			if(cando>=work[j][0])
			{
				money[i]=work[j][1];
				break;
			}
		}
	}
	for(int i=0;i<num_of_worker;i++)
		cout<<money[i]<<endl;
	



}

For this problem, although the above method can only pass 80%, because its complexity is too high O(mn), the use of violent search method. But the above code demonstrates how to write two-dimensional arrays by itself and how to use library functions to sort two-dimensional arrays. The following is a low-complexity optimization program (Access program).

#include<iostream>
#include<algorithm>
using namespace std;
struct job
{
	int dif;
	int money;
};
struct  people
{
	int index;
	int dif;
	int money;
};
bool cmp1(job a,job b)
{
	return a.dif<b.dif;
}
bool cmp2(people a,people b)
{
	return a.dif<b.dif;
}
bool cmp3(people a,people b)
{
	return a.index<b.index;
}
int main()
{
	int num_of_job,num_of_people;
	cin>>num_of_job>>num_of_people;
	job *Job=new job[num_of_job];
	people *People =new people[num_of_people];
	for(int i=0;i<num_of_job;i++)
	{
		cin>>Job[i].dif>>Job[i].money;
	}
	for(int i=0;i<num_of_people;i++)
	{
		cin>>People[i].dif;
		People[i].index=i;
	}
	sort(Job,Job+num_of_job,cmp1);
	sort(People,People+num_of_people,cmp2);
	int j=0;int maxmoney=0;
	for(int i=0;i<num_of_people;i++)
	{
		while(j<num_of_job)
		{
			if(Job[j].dif>People[i].dif)
				break;
			else
			{
				maxmoney=max(maxmoney,Job[j].money);
				j++;
			}
		}
		People[i].money=maxmoney;
	}
	sort(People,People+num_of_people,cmp3);
	for(int i=0;i<num_of_people;i++)
	{
		cout<<People[i].money<<endl;
	}
}

The code is easy to understand, not to mention.

Hash table

This section introduces the function of hash table. There are not too many explanations for the basic implementation (in fact, it depends more on the implementation of hash function). Let me also give a simple example. Given N integers, then M integers, you need to check whether each of M integers has appeared in N integers. Perhaps you can visually think of traversing queries, but when N and M are large, it is obviously unbearable (O (MN). So we can use Hashtable bool array to record whether every number of N numbers has ever appeared. If it appears, it is recorded as true, otherwise it is false. When querying, we can directly use Hashtable bool array to record whether every number of N numbers has appeared or not. This is the simplest example of whether an input number can be used as an array subscript, but if our input is not an integer, but a number of strings and so on, then we need to use hashes to map to an integer, which is what we call a hash table.

Hash functions can be directly addressed by identical or linear changes: H (key) = key, H (key) = a*key+b, and H (key)% mod in addition to residues.

With a little thought, it can be noted that two different keys may get the same hash value by dividing the residue method, and they can't occupy the same position. This situation is called conflict. The solution to the conflict is linear detection method (when the value has been occupied, it moves forward to the next position until there is a vacancy, but it can't occupy the same position). This method can easily lead to push and tie, that is, if several positions are occupied continuously, the efficiency will be reduced to a certain extent.) Similar to the square search method, but relatively different is the chain address method, that is, if the calculated hash value is the same, all keys with the same hash value are linked into a list.

Fourth, breadth-first search

def search(name):
	search_queue  =deque()
	search_queue +=graph[name]
	searched = []
	while search_queue:
		person = search_queue.popleft()
		if not person in searched:
			if person_is_seller(person):
				return True
			else:
				search_queue +=graph[person]
				search.append(person)
	return False
search("you")

The popular thing about breadth-first search is to put all the nearest elements around you in a queue, then pop out one by one to check. For each check, insert the elements around this element that have not been visited into the queue, continue to check, and then check it out to mark it as checked.

For breadth-first search, the common problem is the maze problem. Look at the introduction of algorithmic notebook. I will list a labyrinth title to supplement it:

Problem Description:

Given the size of a maze m*n, "*" represents the wall that cannot be passed, while "." represents the flat ground, S represents the starting point, T represents the end point, in the process of moving, the current position can only move up and down, left and right, to find the shortest path. The maze is as follows: (S coordinates are (2, 2)).

. . . . .

. * . * .

. * S * .

. * * * .

. . . T *

Idea analysis:

The breadth-first search can be used to traverse through the hierarchical order to find the minimum number of steps.

Code:

#include<iostream>
#include<queue>
using namespace std;
const int maxn=1000;
char maze[maxn][maxn];
bool visited[maxn][maxn];
struct node{int x,y;int step;}S,T,Node;
int n,m;
int x[4] = {0,0,1,-1};
int y[4] = {1,-1,0,0};
bool isok(int x,int y)
{
	if(x>=n||x<0||y>=m||y<0)
	{
		return false;
	}
	if(maze[x][y]=='*')
	{
		return false;
	}
	if(visited[x][y]==true)
	{
		return false;
	}
	return true;
}
int BFS()
{
	queue<node> q;
	q.push(S);
	while(!q.empty())
	{
		node top = q.front();
		q.pop();
		if(top.x==T.x&&top.y==T.y)
		{
			return top.step;
		}
		for(int i=0;i<4;i++)
		{
			int tmpx=top.x+x[i];
			int tmpy=top.y+y[i];
			if(isok(tmpx,tmpy))
			{
				Node.x=tmpx;
				Node.y=tmpy;
				Node.step=top.step+1;
				q.push(Node);
				visited[tmpx][tmpy]=true;
			}
		}
	}
	return -1;
}
int main()
{
	cin>>n>>m;
	for(int i=0;i<n;i++)
	{
		getchar();
		for(int j=0;j<m;j++)
		{
			maze[i][j]=getchar();
		}
		maze[i][m+1]='\0';
	}
	cin>>S.x>>S.y>>T.x>>T.y;
	S.step=0;
	cout<<BFS();
}

Examples:

5
5
...
.*.*.
.*S*.
.***.
...T*
2 2 4 3

Output results:

11

5. Dijkstra algorithm

Here is a common example:

Question summary:

Given N cities, M non-directional edges, each city has a certain number of rescue teams, all Bina's border rights are known, now give the starting point and focus, find the shortest path from the starting point to the end and the sum of the number of rescue teams on the shortest path, if there are multiple paths, the maximum number of output.

Ideas:

In order to solve the shortest path, we need to solve two other information, the number of shortest paths and the maximum point weight on the shortest path. So we can make w[u] denote the sum of the maximum point weight from the starting point s to the vertex u, initialize it to 0, and num[u] denote the number of shortest paths from the starting point s to the vertex U. At the time of initialization, only num[s] is 1, and the rest num[u] is 0. Then, when d[v] is updated, two arrays are updated at the same time. The code is as follows:

#include<iostream>
#include<cstring>
using namespace std;
const int MAXV = 510;
const int INF = 1000000000;

//n is the number of vertices, m is the number of edges, st and ed are the starting and ending points, respectively.
//G is adjacent matrix, weight is point weight
//d [] Record the shortest distance, w [] Record the sum of the maximum point weights, num [] Record the number of shortest paths
int n,m,st,ed,G[MAXV][MAXV],weight[MAXV];
int d[MAXV],w[MAXV],num[MAXV];
bool vis[MAXV]={false};

void Dijkstra(int s)
{
	fill(d,d+MAXV,INF);
	memset(num,0,sizeof(num));
	memset(w,0,sizeof(w));
	d[s]=0;
	w[s]=weight[s];
	num[s]=1;
	for(int i=0;i<n;i++)
	{
		int u=-1,MIN=INF;
		//Find an unaccounted vertex
		for(int j=0;j<n;j++)
		{
			if(vis[j]==false&&d[j]<MIN)
			{
				MIN=d[j];
				u=j;
			}
		}
		if(u==-1) return;
		vis[u]=true;
		for(int v=0;v<n;v++)
		{
			if(vis[v]==false&&G[u][v]!=INF)
			{
				if(d[u]+G[u][v]<d[v])
				{
					d[v]=d[u]+G[u][v];
					w[v]=w[u]+weight[v];
					num[v]=num[u];
				}
			
				else if(d[u]+G[u][v]==d[v])
				{
					if(w[u]+weight[v]>w[v])
						w[v]=w[u]+weight[v];
				
					num[v]+=num[u];
			    }
		    }
		}

	}
}

int main()
{
	cin>>n>>m>>st>>ed;
	for(int i=0;i<n;i++)
	{
		cin>>weight[i];
	}
	int u,v;
	fill(G[0],G[0]+MAXV*MAXV,INF);
	for(int i=0;i<m;i++)
	{
		cin>>u>>v;
		cin>>G[u][v];
		G[v][u]=G[u][v];
	}
	Dijkstra(st);
	cout<<num[ed]<<" "<<w[ed]<<endl;
}

Examples:

5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1

Output:

2 4

VI. DYNAMIC PLANNING

The most classical problem of dynamic programming is knapsack problem, including 01 knapsack problem and complete knapsack problem. Here we focus on 01 knapsack problem. 01 problem involves deduction formula. We can refer to the explanation in Algorithmic Diagram, but finally we can get a universal formula (which is also the difference from complete knapsack problem). Of course, 01 knapsack problem can also be used as a classical depth-first search teaching plan. Let's first look at the formulation, and then look at the depth-first search method:

Problem Description:

There are n items. The weight of each item is w[i], and the value is c[i]. There is a backpack with a capacity of V. Ask how to select items and put them into the backpack, so that the value of the items in the backpack is the greatest. There is only one item for each item.

Ideas:

Direct formulas: DP [v] = max (dp [v], DP [v-w [i] + C [i])

Code:

#include<iostream>
using namespace std;
const int maxn=100;
const int maxv=1000;
int w[maxn],c[maxn],dp[maxv];
int main()
{
	int n,V;
	cin>>n>>V;
	for(int i=0;i<n;i++)
	{
		cin>>w[i];
	}
	for(int i=0;i<n;i++)
	{
		cin>>c[i];
	}
	for(int v=0;v<=V;v++)
	{
		dp[v]=0;
	}
	for(int i=1;i<=n;i++)
	{
		for(int v=V;v>=w[i];v--)
		{
			dp[v]=max(dp[v],dp[v-w[i]]+c[i]);
		}
	}
	int max=0;
	for(int v=0;v<=V;v++)
	{
		if(dp[v]>max)
		{
			max=dp[v];
		}
	}
	cout<<max<<endl;
}

Output results:

5 8
3 5 1 2 2
4 5 2 1 3
10

If the depth-first search method is adopted, the following can be seen:

#include<iostream>
using namespace std; 
const int maxn = 30;
int n,V,maxValue = 0;
int w[maxn],c[maxn];
void DFS(int index,int sumW,int sumC)
{
	if(index==n)
	{
		if(sumW<=V&&sumC>maxValue)
			maxValue=sumC;
		return ;
	}
	DFS(index+1,sumW,sumC);
	DFS(index+1,sumW+w[index],sumC+c[index]);
}
int main()
{
	cin>>n>>V;
	for(int i=0;i<n;i++)
	{
		cin>>w[i];
	}
	for(int i=0;i<n;i++)
	{
		cin>>c[i];
	}
	DFS(0,0,0);
	cout<<maxValue<<endl;
}


Examples:

5 8
3 5 1 2 2
4 5 2 1 3
10

Summary:

Generally speaking, this article is an extension of the overall framework of the book Algorithmic Illustration, because I had a certain understanding of the algorithm before, so I only spent one day reading the book, but still find the explanation in the book very interesting and has a good effect on the consolidation of my knowledge, I also think. It took a day or two to sum up the relevant algorithms of this book, which is also written in this article, including some topics of school enrollment and PAT. Because I mainly use c++ as programming language, there are many codes with c++ as the language. But in fact, it is the same as python. We mainly understand the algorithm ideas in this book, and I do not have any. Referring to the expansion of knowledge in books also leaves readers the opportunity to read.

Book files can be downloaded from my GitHub, and the corresponding programs can be typed out. People who want to have a try can visit my GitHub.

GitHub address: https://github.com/Brian-Liew/Algorithm/tree/master/book notes: Algorithmic Diagrams

Topics: Programming github Python REST