算法模板——基础


这里给出的内容,仅是本科计算机数据结构课程中所涉及的基础内容


1. 链表结构

1.1 单链表

C++ 实现

struct ListNode{
    int val;
    ListNode *next;
    ListNode(int x): val(x), next(nullptr){}
};

ListNode *CreateLinkList(int n){
    ListNode *dummyhead = new ListNode(-1);
    ListNode *p = dummyhead;
    int x;
    for(int i=0;i<n;i++)
    {
        cin >> x;
        ListNode *tmp = new ListNode(x);
        p->next = tmp;
        p = tmp;
    }
    return dummyhead;
}

void PrintList(ListNode *head){
    ListNode *p = head->next;
    while(p!=nullptr)
    {
        cout << p->val;
        if(p->next!=nullptr) cout << " -> ";
        p = p->next;
    }
    cout << endl;
}

int GetLength(ListNode *head){
    int cnt = 0;
    ListNode *p = head->next;
    while(p!=nullptr)
    {
        cnt++;
        p = p->next;
    }
    return cnt;
}

bool IsEmpty(ListNode *head){
    if(!head->next) return true;
    return false;
}

ListNode *AddListNode(ListNode *head, int x){
    // 头插入
    ListNode *tmp  = new ListNode(x);
    tmp->next = head->next;
    head->next = tmp;
    return head;
}

ListNode *Find(ListNode *head, int x){
    ListNode *p = head->next;
    if(!p) return nullptr;
    while(p!=nullptr)
    {
        if(p->val==x) return p;
        p = p->next;
    }
    return nullptr;
}

bool DeleteListNode(ListNode* &head, int x){
    ListNode *tmp = Find(head, x);
    if(!tmp) return false;
    ListNode *p = head->next;
    ListNode *pre = head;
    while(p!=nullptr)
    {
        if(p->val==x)
        {
            pre->next = p->next;
            delete p;
            p = nullptr;
            return true;
        }
        else
        {
            pre = p;
            p = p->next;
        }
    }
}

Python

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class LinkList:
    def __init__(self, node=None):
        self.__head = node

    def __len__(self):
        cur = self.__head
        cnt = 0
        while cur:
            cnt += 1
            cur = cur.next
        return cnt

    def empty(self):
        return self.__head == None

    def add(self, x):
        # 头插
        node = ListNode(x)
        node.next = self.__head
        self.__head = node

    def append(self, x):
        # 尾插
        node = ListNode(x)
        cur = self.__head
        if self.empty():
            self.__head = node
        else:
            while cur.next:
                cur = cur.next
            cur.next = node

    def remove(self, x):
        cur = self.__head
        pre = None
        while cur:
            if cur.x == x:
                if cur == self.__head:
                    self.__head = cur.next
                else:
                    pre.next = cur.next
                break
            else:
                pre = cur
                cur = cur.next
    
    def find(self, x):
        cur =  self.__head
        while cur:
            if cur.val == x:
                return True
        return False
    
    def printlist(self):
        cur = self.__head
        while cur:
            print(cur.val)
            cur = cur.next


1.2 双向循环链表

Python

class Node:
    def __init__(self, val):
        self.val = val
        self.prev = self.next = None
 
class DCLinkedList:
    def __init__(self):
         self.dummy = Node(None, None)
         self.dummy.next = self.dummy
         self.dummy.prev = self.dummy
         self.size = 0
    def append(self, node):
        # 尾插入
        node.prev = self.dummy.prev
        node.next = self.dummy 
        node.prev.next = node
        self.dummy.prev = node
        self.size += 1
    def pop(self, node=None):
        # 头删除
        if self.size == 0: return 
        if node is None:
            node = self.dummy.next
        node.prev.next = node.next
        node.next.prev = node.prev
        self.size -= 1
        return node

2. 栈和队列

2.1 栈

vector<int> st(maxsize, 0);
int top = -1;

// 进栈
st[++top] = x;

// 出栈
x = st[top--];

C++ 实现 标准库中的栈

stack<type> st;

st.push();
st.pop();
st.top();
st.size();
st.empty();

// 进栈
st.push(item); 

// 出栈 
x = st.top();
st.pop(); 

直接用 vector

vector<type> st;

// 入栈
st.push_back(item);

// 出栈
x = st.back();
st.pop_back(); 

// 栈空
st.empty();

2.2 队列

个人比较习惯直接就把 C++ 实现 STL 中的 deque 直接当做队列使用

deque<type> dq;

dq.back();
dq.front();
dq.push_back();
dq.pop_front();
dq.size();
dq.empty(); 

2.3 循环队列

少用一个元素空间,约定队列头指针在尾指针的下一位置为满

typedef struct
{
	int nums[maxsize];
	int front, rear;
}cycque;

bool isEmpty(cycque q){
	if(q.front==q.rear) return true;
	return false;
}

bool isFull(cycque q){
	if(q.front==(q.rear+1)%maxsize) return true;
	return false;
}

bool Enque(cycque &q, int x){
	if(isFull(q)) return false;
	q.nums[q.rear] = x;
	q.rear = (q.rear+1)%maxsize;
	return true;
}

bool Deque(cycque &q, int &x){
	if(isEmpty(q)) return false;
	x = q.nums[front];
	q.front = (q.front+1)%maxsize;
	return true;
}

我不将循环队列并入队列哪一些节中的考虑是:实际编程中利用循环队列实现队列是不实用,而循环队列最重要的思想是如何循环使用数组,故将其单独列为一节

3. 树

3.1 二叉树

3.1.1 二叉树存储结构

C++ 实现

struct TreeNode {
	int val;
	TreeNode* left;
	TreeNode* right;
	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

Python

Class TreeNode:
    def __init__(self, value=0, left=None, right=None):
        self.val = value
        self.left = left
        self.right = right

3.1.2 二叉树的遍历

在实现先序遍历、中序遍历和后序遍历前,先理解一下下面这张图

在这里插入图片描述
图 6-9 给出了指针 p p p 沿着图中箭头所指路线游历整个二叉树的过程,并且对于图中的每个结点, p p p 都将经过它 3 次。对应图中右边给出了 p p p 游历整棵树的过程的程序模板。

  • 如果将对结点的访问操作写在 (1)处,则是先序遍历,即对应于图中的每个结点,在 p p p 所走线路经过的标号 1 处(第一次经过这个结点时)对其进行访问
  • 如果将对结点的访问操作写在 (2)处,则是中序遍历,即对应于图中的每个结点,在 p p p 所走线路经过的标号 2 处(第二次经过这个结点时)对其进行访问
  • 如果将对结点的访问操作写在 (3)处,则是后序遍历,即对应于图中的每个结点,在 p p p 所走线路经过的标号 3 处(第三次经过这个结点时)对其进行访问

C++ 实现

先序遍历

// 递归
void preOrder(TreeNode* root){
	if(root)
	{
		Visit(root);
		preOrder(root->left);
		preOrder(root->right); 
	}
} 

// 非递归
void preOrder(TreeNode* root){
	if(root)
	{
		vector<TreeNode*> st;
		TreeNode* tmp;
		
		st.push_back(root);
		while(!st.empty())
		{
			tmp = st.back();
			st.pop_back();
			Visit(tmp);
			if(tmp->left) st.push_back(tmp->left);
			if(tmp->right) st.push_back(tmp->right);
		}
	}
}

中序遍历

解释一下非递归的过程:

如果栈顶结点有左孩子,左孩子入栈;如果栈顶结点没有左孩子,直接出栈并访问栈顶结点,然后检查是否有右孩子,有,右孩子进栈

// 递归
void inOrder(TreeNode* root){
	if(root)
	{
		inOrder(root->left);
		Visit(root);
		inOrder(root->right); 
	}
} 

// 非递归
void inOrder(TreeNode* root){
	if(root)
	{
		vector<TreeNode*> st;
		TreeNode* tmp = root;
		while(!st.empty()||tmp) // 有可能栈空,但是还没有访问去哪 
		{
			while(tmp) // 把左孩子依次入栈 
			{
				st.push_back(tmp);
				tmp = tmp->left;
			}
			if(!st.empty())
			{
				tmp = st.back();
				st.pop_back();
				Visit(tmp);
				tmp = tmp->right;
			}
		}
		
	}
}

后序遍历

后序遍历的非递归实现需要用到两个栈,一个栈 s t 1 st1 st1 用来辅助做逆后序遍历(将先序遍历的左右子树遍历顺序交换)并将遍历结果序列压入栈另一个栈 s t 2 st2 st2,然后将 s t 2 st2 st2中的元素全部出栈,如下例所示

在这里插入图片描述

// 递归
void postOrder(TreeNode* root){
	if(root)
	{
		postOrder(root->left);
		postOrder(root->right); 
		Visit(root);
	}
} 

// 非递归
void postOrder(TreeNode* root){
	if(root)
	{
		vector<TreeNode*> st1;
		vector<TreeNode*> st2;
		TreeNode* tmp;
		st1.push_back(root);
		while(!st1.empty())
		{
			tmp = st1.back();
			st1.pop_back();
			st2.push_back(tmp);
			if(tmp->left) st1.push_back(tmp->left);
			if(tmp->right) st2.push_back(tmp->right);
		}
		while(!st2.empty())
		{
			tmp = st2.back();
			st2.pop_back();
			Visit(tmp);	
		} 
	}
}

层序遍历

vector<vector<int>> levelOrder(TreeNode* root) {
	vector<vector<int>> ret;
	if(!root) return ret;
	
	vector<TreeNode*> q;
	q.push_back(root);
	while(q)
	{
		int n = q.size();
		ret.push_back(vector<int>());
		for(int i=0;i<n;i++)
		{
			auto node = q.front();q.pop();
			ret.back().push_back(node->val);
			if(node->left) q.push_back(node->left);
			if(node->right) q.push_back(node->right);
		}
	}
	return ret;
}

3.1.3 关于二叉树的一些常用模块代码

(1)计算树的高度

int Depth(TreeNode* root){
	int l_depth, r_depth;
	if(!root) return 0;
	l_depth = Depth(root->left);
	r_depth = Depth(root->right);
	return (l_depth>r_depth?l_depth:r_depth)+1;
}

(2)计算树的宽度

int Width(TreeNode* root){
	if(!root) return 0;
	
	deque<TreeNode*> dq;
	dq.push_back(root);
	
	int width =1;
	int curwidth = 1;
	int nextwidth = 0;
	
	while(!dq.empty())
	{
		while(!curwidth)
		{
			TreeNode* tmp = dq.front();
			de.pop_front();
			curwidth--;
			if(tmp->left)
			{
				dq.push_back(tmp->left);
				nextwidth++;
			}
			if(tmp->right)
			{
				dq.push_back(tmp->right);
				nextwidth++;
			}
		}
		if(nexwidth>width) width = nextwidth;
		curwidth = nextwidth;
		nexwidth = 0;
	}
	return width;
}

(3)寻找给定值的结点(含剪枝)

在左子树中找到了给定值的结点,便无需再查找右子树,直接退出本层递即可

void Search(TreeNode* root, TreeNode* &q, int key){
	if(root)
	{
		if(root->val==key) q=root;
		else
		{
			Search(root->left, q, key);
			if(!q) Search(root->right, q, key); 	
		}
	}	
}

(4)先序遍历的第 k 个结点

int n = 0;
void preOrder_K(TreeNode* root, int k){
	if(root)
	{
		n++;
		if(n==k) 
		{
			Visit(root);
			return;
		}
		preOrder_k(root->left);
		preOrder_k(root->right);	
	}
}

3.2 线索二叉树

3.2.1 线索二叉树的结构

C++ 实现

struct TBTreeNode{
	int val;
	int ltag, rtag;
	TBTreeNode* left;
	TBTreeNode* right;
	TBTreeNode(int x) : val(x), ltag(0), rtag(0), left(nullptr), right(nullptr) {}
};

3.2.2 二叉树中序线索化

线索化规则是:给一个游走指针 c u r cur cur 指向当前正在访问的结点, p r e pre pre 指向 c u r cur cur 的前驱结点, c u r cur cur 的左线索如果存在的话则让其指向 p r e pre pre p r e pre pre 的右线索如果存在则指向 c u r cur cur,这样流完成了一对线索的连接

void inThread(TBTreeNode* cur, TBTreeNode* &pre){
	if(cur)
	{
		inThread(cur->left, pre);
		if(!cur->left)
		{
			cur->left = pre;
			cur->ltag = 1;
		}
		if(pre&&(!pre->right))
		{
			pre->right = cur;
			pre->rtag = 1;
		}
		pre = cur;
		inThread(cur->right, pre);
	}
}

中序线索二叉树的一些节本操作

void CreateInThread(TBTreeNode* root){

	// 通过中序遍历建立中序线索二叉树
	
	TBTreeNode* pre = nullptr;
	if(root)
	{
		inThread(root, pre);
		pre->right = nullptr; // 后处理中序最后一个结点 
		pre->rtag = 1;
	}
}

TBTreeNode* First(TBTreeNode* cur){
	
	// 求以当前结点为根结点的中序线索二叉树中,中序序列下的第一个结点
	 
	while(!cur->ltag)
		cur = cur->left;
	return cur;
}

TBTreeNode* Last(TBTreeNode* cur){
	
	// 求以当前结点为根结点的中序线索二叉树中,中序序列下的最后一个结点
	 
	while(!cur->rtag)
		cur = cur->right;
	return cur;
}

TBTreeNode* Next(TBTreeNode* cur){
	
	// 求中序线索二叉树中,当前结点在中序下的后继结点 
	
	if(!cur->rtag) return First(cur->right);
	else return cur->right; 
}

TBTreeNode* Prior(TBTreeNode* cur){
	
	// 求中序线索二叉树中,当前结点在中序下的前驱结点 
	
	if(!cur->ltag) return Last(cur->left);
	else return cur->left; 
}

void inOrder_TBTree(TBTreeNode* root){
	for(auto cur=First(root);cur!=nullptr;cur=Next(cur))
		Visit(cur);
} 

3.2.3 前序线索二叉树

void preThread(TBTreeNode* cur, TBTreeNode* &pre){
	if(cur)
	{
		if(!cur->left)
		{
			cur->left = pre;
			cur->ltag = 1;
		}
		if(pre&&(!pre->right))
		{
			pre->right = cur;
			pre->rtag = 1;
		}
		pre = cur;
		if(!cur->ltag) preThread(cur->left, pre);
		if(!cur->rtag) preThread(cur->right, pre);
	}
}

前序线索二叉树上的前序遍历

void preOrder_TBTree(TBTreeNode* root){
	if(root)	
	{
		TBTreeeNode* cur = root;
		while(cur)
		{
			while(!cur->ltag)
			{
				Visit(cur);
				cur = cur->left;
			}
		} 
		Viste(cur); // 当前结点的左指针必为线索 
		cur = cur->right; // 当前结点的左孩子不存在,则右指针若非空,则不论是否为线索都指向其后继 
	}
}

3.2.4 后序线索二叉树

void postThread(TBTreeNode* cur, TBTreeNode* &pre){
	if(cur)
	{
		postThread(cur->left, pre);
		postThread(cur->right, pre);
		if(!cur->left)
		{
			cur->left = pre;
			cur->ltag = 1;
		}
		if(pre&&(!pre->right))
		{
			pre->right = cur;
			pre->rtag = 1;
		}
		pre = cur;
	}
}

4. 图

4.1 存储结构

这里只给出邻接表,邻接矩阵就是二维数组,不再赘述

C++ 实现

typedef struct ArcNode{ // 表结点 
	int adjvex;
	ArcNode* next;
	int info;
}ArcNode;

typedef struct VNode{ // 表头结点 
	int info;
	ArcNode* first; 
}VNode;

typedef struct Graph{
	VNode adjlist[maxsize];
	int n, e;
};

Python

from collections import defaultdict

class Graph():
    
    def __init__(self, v):
        self.graph = defaultdict(list)
        self.v = v
    
    def addedge(self, u, v):
        self.graph[u].append(v)

g = Graph(v)
g.addedge(x, y)   

4.2 遍历

4.2.1 连通图

BFS

C++ 实现(邻接表)

vector<int> visited(maxsize, 0);

void BFS(AGraph* G, int v){
	ArcNode *p;
	deque<int> dq;
	Visit(v);
	visited[v] = 1;
	dq.push_back(v);
	while(!dq.empty())
	{
		int tmp = dq.front();
		dp.pop_front();
		p = G->adjlist[tmp].first;
		while(p)
		{
			if(!visited[p->adjvex])
			{
				Visit(p->adjvex);
				visited[p->adjvex] = 1;
				dq.push_back(p->adjvex);
			}
			p = p->next;
		}
	}
}

Python(邻接矩阵)

def BFS(graph, s):
    q = []
    q.append(s)
    visited = set()
    visited.add(s)
    while q:
        vertex = q.pop(0)
        nodes = graph[vertex]
        for node in nodes:
            if node not in visited:
                q.append(node)
                visited.add(node)
        Visit(vertex)

DFS

C++ 实现(邻接表)

// 递归
vector<int> visited(maxsise, 0);

void DFS(AGraph* G, int v){
	ArcNode* p;
	visited[v] = 1;
	Visit(v);
	p = G->adjlist[v].first;
	while(p)
	{
		if(!visited[p->adjvex])
		{
			DFS(G, p->adjvex);
			p = p->next;
		}
	}
} 

// 借助栈
void DFS(AGraph* g, int v){
	ArcNode* p;
	vector<int> st;
	vector<int> visited(maxsize);
	Visit(v);
	visited[v] = 1;
	st.push_back(v);
	while(st)
	{
		int k = st.back();
		p = g->adjlist[k].fisrt;
		while(p && visited[v->adjvex]) p = p->next;
		if(!p) st.pop_back();
		else
		{
			Visit(p->adjvex);
			visited[p->adjvex] = 1;
			st.push_back(p->adjvex);
		}
	}	
}

Python(邻接矩阵)

# 递归
def DFS(graph, s, path=[]):
    path.append(s)
    for i in graph[s]:
        if i not in path:
            DFS(graph, i, path)
    return path

# 借助栈
def DFS(graph, s):
    st = []
    st.append(s)
    visited = set()
    visited.add(s)
    while st:
        vertex = st.pop()
        nodes = graph[vertex]
        for node in nodes:
            if node not in visited:
                st.append(node)
                visited.add(node)
        Visit(vertex)

4.2.2 非连通图

DFS(C++ 实现)

void DFS_disconnected(AGraph* G){
	vector<int> visited(G->n, 0);
	for(int i=0;i<G->n;i++)
		if(!visited[i]) DFS(G, i);
} 

BFS

 void BFS_disconnected(AGraph* G){
 	vector<int> visited(G->n, 0);
	for(int i=0;i<G->n;i++)
		if(!visited[i]) BFS(G, i);
} 

4.3 最小生成树

4.3.1 普利姆算法

void Prim(Mgraph G, int start, int &sum){
	int mmin, v, tmp;
	vector<int> lowcost(masize, 0);
	vector<int> inMST(masize, 0);
	
	for(int i=0;i<G.n;i++)
		lowcost[i] = G.edges[start][i];
	
	inMST[start] = 1;
	v= start, sum = 0;
	for(int i=0;i<G.n-1;i++)
	{
		min = INT_MAX;
		
		// 选出侯选边中的最小者 
		for(int j=0;j<G.n;j++)
		{
			if(!inMST[j]&&lowcost[j]<mmin)
			{
				mmin = lowcost[j];
				tmp  = j;
			}
		}
		
		inMST[tmp] = 1;
		sum += mmin; 
		v = tmp;
		
		// 以刚并入的顶点为媒介更新侯选边 
		for(int j=0;j<G.n;j++)
			if(!inMST[j]&&G.edges[v][j]<lowcost[j])
				lowcost[j] = G.edges[v][j];
		
	}
} 

4.3.2 克鲁斯卡尔算法

// 假设 road 中已经存放了图中各边及其所连接的两个顶点的信息

typedef struct{
	int from, to;
	int w;
}Road;

Road[maxsize];
vector<int> MFS(maxsize, 0);

int getRoot(int node){
	while(node!=MFS[node]) node = MFS[node];
	return node;
}

void Kruskal(MGraph G, int &sum, Road road[]){
	int from, to;
	
	for(int i=0;i<G.n;i++) MFS[i] = i;
	sort(road, G.e) // 具体实现的排序函数,非标准库函数
	for(int i=0;i<G.e;i++)
	{
		from = getRoot(road[i].from);
		to = getRoot(road[i].to);
		if(from!=to)
		{
			MFS[from] = to;
			sum += road[i].w; // 此处是具体对生成树的操作,不固定 
		}
	} 
}

4.4 最短路径

4.4.1 迪杰斯特拉

C++ 实现

void PrintPath(vector<int> path, int leaf){
	int tmp;
	vector<int> st;
	
	while(path[leaf]!=-1)
	{
		st.push_back(leaf);
		leaf = path[leaf];
	}
	st.push_back(leaf);
	while(!st.empty())
	{
		tmp = st.back();
		st.pop_back();
		cout << tmp << " ";
		cout << endl;
	 } 
} 

Python 实现

inf = float('inf')

def dijkstra(graph_matix, s):
    dis = graph_matix[s]
    n = len(dis)
    flag = [0 for _ in range(n)]
    for i in range(n - 1):
        mmin = inf 
        for j in range(n):
            if flag[j] == 0 and dis[j] < mmin:
                mmin = dis[j]
                u = j
        flag[u] = 1
        for v in range(n):
            if flag[v] == 0 and graph_matix[u][v]< inf:
                if dis[v] > dis[u] + graph_matix[u][v]:
                    dis[v] = dis[u] + graph_matix[u][v]
                    
    return dis 

4.4.2 弗洛伊德

C++ 实现

void Floyd(Mgraph G, vector<vector<int>> &Path){
	vector<vector<int>> A(maxsize, vector<int>(maxsize, 0));
	for(int i=0;i<G.n;i++)
	{
		for(int j=0;j<G.n;j++)
		{
			A[i][j] = G.edges[i][j];
			Path[i][j] = -1;
		}
	 } 
	 
	for(int k=0;k<G.n;k++)
	{
		for(int i=0;i<G.n;i++)
		{
			for(int j=0;j<G.n;j++)
			{
				if(A[i][j]>A[i][k]+A[k][j])
				{
					A[i][j] = A[i][k]+A[k][j];
					Path[i][j] = k;
				}
			}
		}
	}
}

void PrintPath(int u, int v, vector<vector<int>> path){
	
	if(path[u][v]==-1) ... // 直接输出,表示 u、v 之间没有中间顶点 
	else
	{
		int mid = path[u][v];
		PrintPath(u, mid, path);
		PrintPath(mid, v, path); 
	}
}

Python 实现

inf = float('inf')

def floyd(graph_matrix, s):
    n = len(graph_matrix[0])
    path = [[inf  for _ in range(n)] for _ in range(n)]
    for k in range(n):
        for i in range(n):
            for j in range(n):
                if graph_matrix[i][j] > graph_matrix[i][k] + graph_matrix[k][j]:
                    graph_matrix[i][j] = graph_matrix[i][k] + graph_matrix[k][j]
                    path[i][j] = k
    return graph_matrix, path

4.5 拓扑排序

typedef struct{
	int info;
	int cnt; // 计算入度 
	ArcNode* first;
}VNode;

// 假设顶点的度已经记录在 cnt 中 

bool TopSort(AGraph* G){
	vector<int> st;
	ArcNode* p;
	int tmp, j, n = 0;
	
	// 入度为 0 的顶点入栈
	for(int i=0;i<G->n;i++)
		if(!G->adjlist[i].cnt) st.push_back(i);
		
	while(!st.empty())
	{
		tmp = st.back();
		st.pop_back();
		n++;
		cout << tmp << " ";
		p = G->adjlist[i].first;
		while(p)
		{
			j = p->adjvex;
			G->adjlist[j].cnt--;
			if(!G->adjlist[j].cnt) st.push_back(j);
			p = p->next;
		}
	 } 
	 
	if(n==G->n) return true;
	else return false;
} 

4.6 有向图检测环

着色法

对于图中的任意一个节点,我们定义三种状态,即:
「未搜索」:我们还没有搜索到这个节点;

「搜索中」:我们搜索过这个节点,但还没有回溯到该节点,即该节点还没有入栈,还有相邻的节点没有搜索完成);

「已完成」:我们搜索过并且回溯过这个节点,即该节点已经入栈,并且所有该节点的相邻节点都出现在栈的更底部的位置,满足拓扑排序的要求。

于是算法流程就是:

我们将当前搜索的节点 u u u 标记为「搜索中」,遍历该节点的每一个相邻节点 v v v

  • 如果 v v v 为「未搜索」,那么我们开始搜索 v v v,待搜索完成回溯到 u u u

  • 如果 v v v 为「搜索中」,那么我们就找到了图中的一个环,因此是不存在拓扑排序的;

  • 如果 v v v 为「已完成」,那么说明 v v v 已经在栈中了,而 u u u 还不在栈中,因此 u u u 无论何时入栈都不会影响到 ( u , v ) (u, v) (u,v) 之前的拓扑关系,以及不用进行任何操作。

实际编程中没有必要用到栈,只用标记好状态就行

C++ 实现,图结构为矩阵

vector<int> visited(maxsize, 0);
bool invalid; 

void dfs(int u) {
	visited[u] = 1; // 将节点标记为「搜索中」
	for(int v: edges[u])
	{
		if(visited[v]==0) 
		{
			dfs(v);
			if(invalid) return;
		}
		else if(visited[v]==1)
		{
			invalid = true;
			return;
		}
	}
	visited[u] = 2; // 将节点标记为「已完成」
}

bool  iscyclic(vector<vector<int>& edges){
	for(int i=0;i<edges.size() && !invalid;i++)
		if(!visited[i]) dfs(i);
	if(invalid) return true;
	return false;
}

Python 实现, 图结构为邻接表

def dfs(self, u, color):
	color[u] = "gray" # gary: this vertex is being processed
    for v in self.graph[u]:
    	if color[v] == 'gray': return True 
        if color[v] == 'white' and self.dfs(v, color) == True: 
        	# white: vertex is not processed yet 
            self.cyc.append(v)
            return True 
	color[u] = "balck" # black:vertex and all its descendants are processed
    return Fasle
   
def iscyclic(self):
	for i in range(self.v):
    	color = ['white'] * self.v
        if color[i] == 'white':
        	if self.dfs(i, color) == True:
            	return True
    return False

利用拓扑排序

很简单就是完成拓扑排序之后还有节点未被加入到拓扑序列,那么就说明有环

C++ 实现,图结构为矩阵

vector<int> indeg;
vector<int> res;

bool iscyclic(vector<vector<int>>& edges){
	queue<int> q;
    for(int i=0;i<edges.size();i++)
        if(indeg[i]==0) q.push(i);
    
	while(!q.empty())
    {
        int u = q.front();
        q.pop();
        res.push_back(u);
        for(int v: edges[u])
        {
            indeg[v]--;
            if(!indeg[v]) q.push(v);
        }
    }

    if(res.size()!= numCourses) return false;
    return true;	
}

Python实现,图结构为矩阵

def findcyc(G):
    node_set = set()
    r = len(G)
    have_in_zero = True
    while have_in_zero:
        have_in_zero = False
        for i in range(r):
            if i not in node_set and not any([row[i] for row in G]):
                node_set.add(i)
                for j in range(len(G[i])):
                    if G[i][j]: G[i][j] -= 1
                have_in_zero = True
                break
    return False if len(node_set) == r else True

Reference

5. 排序算法

5.1 插入排序

5.1.1 直接插入排序

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

5.1.2 折半插入

void binaryinsertsort(vector<int> &nums)
{
	for(int i=1;i<(int)nums.size();i++)
	{
		int key = nums[i];
		in l = 0, r = i-1;
		while(l<=r)
		{
			int mid = l+(l+r)/2;
			if(nums[mid]>key) r = mid-1;
			else l = mid+1;
		}
		// at the end of the process of biary, l = the index of the elements which is greater than key
		for(int j=i-1;j>=l;j--) nums[j+1] = nums[j];
		nums[l] = key;
	}
}

5.2 希尔排序

在这里插入图片描述

void shellsort(vector<int> &nums)
{
	int gap = (int)nums.size()>>1;
	while(gap>0)
	{
		// sort every subsequence
		for(int i=0;i<gap;i++)
		{
			// the porecess of insert sort 
			for(int j=i+gap;j<(int)nums.size;j+=gap)
			{
				int tmp = j;
				while(tmp>i && nums[tmp]<nums[tmp-gap])
				{
					swap(nums[tmp], nums[tmp-gap]);
					tmp -= gap;	
				}
			}
		}
		gap >>= 1;
	}
}

5.3 冒泡排序

5.3.1 基础冒泡


下面代码的实现不同于最简单的冒泡排序,这里加入了一个border用来记录上一次最后交换的那个位置,下一轮交换只需要进行到这个位置即可

void bubblesort(vector<int>& nums) 
{
	for(end=(int)nums.size-1;end>0;end--)
	{
		int border = 0;
		for(int i=1;i<=end;i++) 
		{
			if(nums[i-1]>nums[i])
			{
				swap(nums[i-1], nums[i]);
				border = i;
			}
		}
		end = border;
	}
}

5.3.2 鸡尾酒排序(冒泡的改进)

鸡尾酒排序也叫定向冒泡排序,在大的往下沉的同时,小的往上浮

void cocktailsort(vector<int>& nums)
{
	int l = 0, r = (int)nums.size()-1;
	while(l<r)
	{
		for(int i=l;i<r;i++)
			if(nums[i]>num[i+1]) swap(nums[i], nums[i+1]);
		r--;
		for(int i=r;i>l;i--)
			if(nums[i]<nums[i-1]) swap(nums[i], nums[i-1]);
		l++; 
	}
}

5.4 快速排序

5.4.1 基础快排

int partition(vector<int>& nums, int l, int r)
{
	int pivot = l;
	int key = nums[l];
	for(int i=l;i<=r;i++)
	{
		if(nums[i]<key) swap(nums[i], nums[++pivot]);
	}
	swap(nums[pivot], nums[l]);
	return pivot;
}

void qicksort(vector<int>& nums, int l, int r)
{
	if(l<r)
	{
		int pos = partition(nums, l, r);
		quicksort(nums, l, pos-1);
		quicksort(nums, pos+1, r);
	}
}

void sortArray(vector<int>& nums) 
{
	if(nums.size()<=1) return;
	quicksort(nums, 0, (int)nums.size()-1);
}

5.4.2 随机快排

解决普通快排在部分有序数组中进行排序效率低下的问题

class Solution {
public:
    int partition(vector<int>& nums, int l, int r){
        int pivot = l;
        for(int i=l+1;i<=r;i++)
        {
            if(nums[i]<nums[l]) swap(nums[i], nums[++pivot]);
        }
        swap(nums[pivot], nums[l]);
        return pivot;
    }
    int randomized_partition(vector<int>& nums, int l, int r){
        int i = rand() % (r-l+1)+l;
        swap(nums[l], nums[i]);
        return partition(nums, l, r);
    }
    void randomized_quicksort(vector<int>& nums, int l, int r)
    {
        if(l<r)
        {
            int pos = randomized_partition(nums, l, r);
            randomized_quicksort(nums, l, pos-1);
            randomized_quicksort(nums, pos+1, r);
        }
    }
    vector<int> sortArray(vector<int>& nums) {
        srand((unsigned)time(NULL));
        randomized_quicksort(nums, 0, (int)nums.size()-1);
        return nums;
    }
};

5.5 选择排序

在这里插入图片描述
注意图上示意的选最小,下面代码实现是选最大

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

5.6 堆排(大顶堆)

class Solution {
public:

    void adjustheap(vector<int>& nums, int i, int len){
        int tmp = nums[i];
        for(int k=i*2+1;k<len;k=k*2+1) // 从i节点的左节点开始重建
        {
            if(k+1<len && nums[k]<nums[k+1]) k++; // 如果左子节点小于右子节点,则让k指向右子节点
            if(nums[k]>tmp) // 如果子节点大于父节点,将子节点赋值给父节点(不用进行交换)
            {
                nums[i] = nums[k];
                i = k;
            }
            else break;
        }
        nums[i] = tmp; // 将tmp放到最终位置
    }

    void heapsort(vector<int>& nums){
        //构建大顶堆
        for(int i=nums.size()/2-1;i>=0;i--) 
            adjustheap(nums, i, nums.size());
        // 弹出最大元素+调整堆
        for(int i=nums.size()-1;i>=1;i--)
        {
            swap(nums[0], nums[i]); // 弹出最大顶堆的堆顶放在最后
            adjustheap(nums, 0, i); // 重建大顶堆
        }
    }

    vector<int> sortArray(vector<int>& nums) {
        heapsort(nums);
        return nums;
    }
};

Python 实现(借助heapq模块)

class Solution:

    def heapsort(self, nums):
        h = []
        for i in nums:
            heapq.heappush(h, i)
        return [heapq.heappop(h) for _ in range(len(h))]

    def sortArray(self, nums: List[int]) -> List[int]:
        nums = self.heapsort(nums)
        return nums

5.7 归并

在这里插入图片描述

void merge(vector<int>& nums, int left, int mid, int right)
{
	vector<int> help(right-left+1);
	int k = 0;
	int p1 = left, p2 = mid+1;
	while(p1<=mid && p2<=right)
		help[k++] = nums[p1]<=nums[p2]?nums[p1++]:nums[p2++]; // 若左右两边相等,则先拷贝左边的,以实现稳定排序
	while(p1<=mid) // 左边剩余部分
		help[k++] = nums[p1++];
	while(p2<=right) // 右边剩余部分 
		help[k++] = nums[p2++];
	for(int i=0;i<k;i++)
		nums[i+left] = help[i];
}

void mergesort(vector<int>& nums, int left, int right) 
{
	if(left>=right) return;
	int mid  = (left+right)/2;
	
	mergesort(nums, left, mid);
	mergesort(nums, mid+1, right);
	// 这是一个优化,若nums[left, mid]nums[mid+1, right]已经有序,则不需要再排序了 
	if(nums[mid]>nums[mid+1]) merge(nums, left, mid, right);
}

vector<int> sortArray(vector<int>& nums)
{
	mergesort(nums, 0, (int)nums.size()-1);
	return nums;
}

5.8 计数排序

计数排序算法操作起来只有三步,看完秒懂!

  1. 根据待排序集合中最大元素和最小元素的差值范围确定申请的桶个数;
  2. 遍历待排序集合,将每一个元素统计到对应的桶中;(此步完成后每个桶里面的数字代表了此桶对应元素出现的次数。)
  3. 从小到大遍历一遍所有桶,如果桶中有元素,那么就往排序结果里添加上对应个数的该桶对应的元素。

在这里插入图片描述

void countsort(vector<int>& nums)
{
	int mmin = INT_MAX;
	int mmax = INT_MIN;
	for(int num: nums)
	{
		mmin = min(mmin, num);
		mmax = max(mmax, num);
	}
	vector<int> count(mmax-mmin+1);
	for(int num: nums)
		count[num-mmin]++;
	int cur = 0;
	for(int i=0;i<(int)count.size();i++)
	{
		while(count[i]>0)
		{
			nums[cur++] = i+mmin;
			count[i]--;
		} 
	}
}

5.9 桶排

在这里插入图片描述

void bucket_insert(list<int>& bucket, int val)
{
	auto iter = bucket.begin();
	// insert会在iter之前插入数据,以保证稳定排序 
	while(iter!=bucket.end() && val >= *iter) iter++;
	bucket.insert(iter, val);
	
}

void bucketsort(vector<int>& nums)
{
	int len = nums.size();
	if(len<=1) return; 
	int mmin = nums[0], mmax = mmin;
	for(int i=1;i<len;i++)
	{
		mmin = min(mmin, nums[i]);
		mmax = max(mmax, nums[i]);
	}
	int k = 10;
	int bucketnum = (mmax-mmin)/k + 1; // 向上取整,例如[0,9]有10个数,(9 - 0)/k + 1 = 1
	vector<list<int>> buckets(bucketnum);
	for(int i=0;i<len;i++)
	{
		int value = nums[i];
		bucket_insert(buckets[(value-mmin)/k], value);
	}
	int indx = 0;
	for(int i=0;i<bucketnum;i++)
	{
		if(buckets[i].size())
		{
			for(auto value: buckets[i])
				nums[indx++] = value;
		}
	}
}

5.10 基数排序

在这里插入图片描述

void radixsort(vector<int>& nums)
{
	if(!nums.size()) return;
	int mmax = INT_MIN;
	stringstream ss;
	string s;
	ss << mmax;
	ss >> s;
	int maxdigit = s.length(); // 最大位数
	vector<list<int>> buckets(10);
	int div = 1, mod = 10;
	for(int i=0;i<maxdigit;i++)
	{
		for(int num: nums)
			buckets[num%mod/div].push_back(num);
		div *= 10;
		mod *= 10;
		int idx = 0;
		for(int j=0;j<10;j++)
		{
			for(auto val: buckets[j])
			{
				nums[idx] = val;
				idx += 1;
			}
			buckets[j].clear();
		}
	}
}

Reference

6. 查找

6.1 二分查找

int binary_search(vector<int> nums, int target) {
    int lo = 0, hi = nums.length - 1, mid = 0;
    while (lo <= hi) 
    {
        mid = lo + (hi - lo) / 2;
        if (nums[mid] == target)  return mid;
        if (nums[mid] < target)  lo = mid + 1;
        else hi = mid - 1;
    }
    return -1;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值