这里给出的内容,仅是本科计算机数据结构课程中所涉及的基础内容
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 计数排序
计数排序算法操作起来只有三步,看完秒懂!
- 根据待排序集合中最大元素和最小元素的差值范围确定申请的桶个数;
- 遍历待排序集合,将每一个元素统计到对应的桶中;(此步完成后每个桶里面的数字代表了此桶对应元素出现的次数。)
- 从小到大遍历一遍所有桶,如果桶中有元素,那么就往排序结果里添加上对应个数的该桶对应的元素。
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;
}