Кратко
В процессе программирования одной из проблем, с которыми обычно приходится сталкиваться, является узкое место в производительности. Много раз рассматривается вопрос о том, как выполнить горизонтальное расширение, но игнорируется самый основной вопрос: действительно ли система достигла узкого места?
Узкие места в производительности обычно проявляются в чрезмерном потреблении ресурсов и недостаточной производительности внешней системы обработки или в низком потреблении ресурсов, но скорость отклика программы все еще не соответствует требованиям.
И способ настройки
Ищите код, который чрезмерно потребляет ресурсы, и ищите причины и код, которые недоиспользуют ресурсы, но выполнение программы замедляется.Основание определяет высотуВозьмем для сравнения автомобиль.Обычно вы не понимаете принципы работы коробок передач и двигателей, но все же можете водить.Тот же человек, который не понимает структуры данных и алгоритмы, может еще и программировать. Многие думают, что базовые структуры данных и операции инкапсулированы в языки высокого уровня, а библиотечные функции можно вызывать напрямую, так нужно ли изучать структуры данных?
数据结构+算法=程序
Обычно в программе, сталкиваясь с практической проблемой, полностью используют структуру данных, эффективно сохраняют данные и взаимосвязь между ними в компьютере, а затем выбирают соответствующую стратегию алгоритма и эффективно реализуют ее с помощью программы. способ повысить производительность программы Основной способ.
- Как эффективно хранить данные, какую алгоритмическую сложность создают различные структуры данных и существует ли лучший метод хранения для повышения эффективности алгоритма?
Если у вас нет соответствующих знаний, как выполнить вышеуказанную реализацию? Если он отделен от исходного вызова, как завершить эффективную реализацию программы? И все реализации приложений зависят от основы, которая представляет собой структуру данных и алгоритм. Только поняв это, мы можем быть свободны от любой технологической эволюции. Все говорят, что основание определяет высоту!
Базовые концепты
Структура данных представляет собой хранение и организацию данных в компьютере, в основном описывая взаимосвязь между элементами данных и их расположением. Выбор подходящей структуры данных может повысить эффективность выполнения (временная сложность) и эффективность хранения (пространственная сложность) компьютерных программ.
数据结构的三种层次
:
- Логическая структура — уровень абстракции:Он в основном описывает логическую связь между элементами данных.
-
集合结构(集)
:Все элементы принадлежат совокупности и не имеют никакого другого отношения, кроме принадлежности к одному и тому же множеству. Структура коллекции не подчеркивает никакой ассоциативности между элементами. -
线性结构(表)
:Между элементами данных существует контекстная связь один к одному. В структуре должен быть уникальный первый элемент и уникальный завершающий элемент. -
树形结构(树)
:Связь «один ко многим» между элементами данных -
网状结构(图)
:Существует связь «многие ко многим» между элементами данных в графе или сетевой структуре.
- Физическая структура — уровень хранения:Он в основном описывает позиционные отношения между элементами данных.
-
顺序结构
:Последовательная структура заключается в использовании группы последовательных единиц хранения для последовательного хранения логически смежных элементов.
优点
:Вам нужно только подать заявку на пространство памяти для хранения самих данных, поддерживать доступ по индексу, а также можно добиться произвольного доступа.
缺点
:Непрерывное пространство должно выделяться статически, а использование пространства памяти относительно низкое. Вставка или удаление может потребовать перемещения большого количества элементов, что неэффективно. -
链式结构
:Цепная структура хранения не использует непрерывное пространство для хранения элементов структуры, а создает узел для каждого элемента. Помимо хранения самих данных, узел также должен хранить указатель на следующий узел.
优点
:Неиспользование непрерывного пространства хранения приводит к высокому использованию пространства памяти, преодолевая недостаток прогнозирования количества элементов в последовательной структуре хранения. При вставке или удалении элементов нет необходимости перемещать большое количество элементов.
缺点
:Дополнительное пространство требуется для выражения логической связи между данными, Доступ по подписке и произвольный доступ не поддерживаются. -
索引结构
:В дополнение к установлению информации об узлах хранения также создается дополнительная индексная таблица для маркировки адресов узлов. Индексная таблица состоит из нескольких индексных записей.
优点
:Номер индекса узла используется для определения адреса хранения узла и извлечения блока скорости.
`Минусы:Добавление дополнительной индексной таблицы потребует больше места для хранения. -
散列结构
:Адрес хранения узла определяется значением ключа узла. Помимо использования для поиска, хеширование также может использоваться для хранения.
优点
:Хеширование является развитием метода хранения массива.Некоторые элементы хранимого массива используются в качестве входных данных функции сопоставления.Выводом функции сопоставления является место хранения данных.По сравнению с массивом скорость доступа к данным хэша выше, чем у массива.
缺点
:Сортировка не поддерживается и обычно требует больше места, чем при использовании линейной таблицы, а ключи записей не могут повторяться.
- Операционная структура - уровень реализации:Основное описание - как реализовать структуру данных
- Распределяйте ресурсы, стройте структуры, освобождайте ресурсы
- Вставить и удалить
- получить и повторить
- Изменить и отсортировать
сравнение структур данных
Часто используемые структуры данных:
Выбор структуры данных:
О символ
O выражает временную сложность алгоритма, что очень полезно при анализе сложности алгоритма. Общие:
-
O(1)
: Наименьшая сложность, независимо от количества данных, затраты времени остаются прежними, и могут быть получены после одного расчета. Алгоритм хеширования обычно O (1) -
O(n)
: Линейный, n представляет количество данных, эквивалент увеличивается, затраты времени также увеличиваются, есть общие алгоритмы обхода -
O(n²)
: Квадрат, это означает, что время занимает квадрат, умноженный на n, когда вы видите встроенный цикл цикла, в основном этот алгоритм является квадратным уровнем, например: пузырьковая сортировка и т. д. -
O(log n)
: логарифмический, обычноx= n, то число x называется логарифмом числа n по основанию a, то есть x=logan, где a обычно равно 2, например: число увеличивается в 8 раз, а потребление времени увеличивается только в 3 раза.Двоичный поиск представляет собой логарифмический алгоритм, и каждый раз отсеивается половина -
O(n log n)
: Линейный логарифм, то есть n раз log n. Согласно приведенным выше данным, данные увеличиваются в 8 раз, а потребление времени составляет 8 * 3 = 24 раза. Сортировка слиянием - это линейный логарифмический алгоритм.
Array
В Java массив используется для хранения коллекции данных одного и того же типа.Обратите внимание, что могут храниться данные только одного типа.
//只声明了类型和长度
数据类型 [] 数组名称 = new 数据类型[数组长度];
//声明了类型,初始化赋值,大小由元素个数决定
数据类型 [] 数组名称 = {数组元素1,数组元素2,......}
大小固定,不能动态扩展(初始化给大了,浪费;给小了,不够用),插入快,删除和查找慢
public class Array {
private int[] intArray;
private int elems;
private int length;
public Array(int max) {
length = max;
intArray = new int[max];
elems = 0;
}
/**
* 添加
* @param value
*/
public void add(int value){
if(elems == length){
System.out.println("error");
return;
}
intArray[elems] = value;
elems++;
}
/**
* 查找
* @param searchKey
* @return
*/
public int find(int searchKey){
int i;
for(i = 0; i < elems; i++){
if(intArray[i] == searchKey)
break;
}
if(i == elems){
return -1;
}
return i;
}
/**
* 删除
* @param value
* @return
*/
public boolean delete(int value){
int i = find(value);
if(i == -1){
return false;
}
for (int j = i; j < elems-1; j++) {
//后面的数据往前移
intArray[j] = intArray[j + 1];
}
elems--;
return true;
}
/**
* 更新
* @param oldValue
* @param newValue
* @return
*/
public boolean update(int oldValue,int newValue){
int i = find(oldValue);
if(i == -1){
return false;
}
intArray[i] = newValue;
return true;
}
/**
* 显示所有
*/
public void display(){
for(int i = 0 ; i < elems ; i++){
System.out.print(intArray[i]+" ");
}
System.out.print("\n");
}
/**
* 冒泡排序
* 每趟冒出一个最大数/最小数
* 每次运行数量:总数量-运行的趟数(已冒出)
*/
public void bubbleSort(){
for(int i = 0; i < elems-1; i++){//排序趟数 n-1次就行了
System.out.println("第"+(i+1)+"趟:");
for(int j = 0; j < elems-i-1; j++){//每趟次数 (n-i) -1是防止下标越界,后面赋值用到了+1
if(intArray[j] > intArray[j+1]){ //控制按大冒泡还是按小冒泡
int temp = intArray[j];
intArray[j] = intArray[j+1];
intArray[j+1] = temp;
}
display();
}
}
}
/***
* 选择排序
* 每趟选择一个最大数/最小数
* 每次运行数量:总数量-运行的趟数(已选出)
*/
public void selectSort(){
for(int i = 0; i < elems-1; i++) {//排序趟数 n-1次就行了
int min = i;
for(int j = i+1; j < elems; j++){ //排序次数 每趟选择一个数出来,-1次
if(intArray[j] < intArray[min]){
min = j;
}
}
if(i != min ){
int temp = intArray[i];
intArray[i] = intArray[min];
intArray[min] = temp;
}
display();
}
}
/**
* 插入排序
* 每趟选择一个待插入的数
* 每次运行把待插入的数放在比它大/小后面
*/
public void insertSort(){
int j;
for(int i = 1; i < elems; i++){
int temp = intArray[i];
j = i;
while (j > 0 && temp < intArray[j-1]){
intArray[j] = intArray[j-1];
j--;
}
intArray[j] = temp;
}
display();
}
public static void main(String[] args) {
Array array = new Array(10);
array.add(6);
array.add(3);
array.add(8);
array.add(2);
array.add(11);
array.add(5);
array.add(7);
array.add(4);
array.add(9);
array.add(10);
// array.bubbleSort();
// array.selectSort();
array.insertSort();
// array.display();
// System.out.println(array.find(4));
// System.out.println(array.delete(1));
// array.display();
// System.out.println(array.update(2,6));
// array.display();
}
}
Stack
- Стек (стек) также известен как стек или стек.Являясь структурой данных, стек хранит данные по принципу «первым пришел, последним вышел». в верхней части стека.
- Stack в Java — это подкласс Vector, который определяет только конструктор по умолчанию для создания пустого стека.
- Стек — это набор элементов, который состоит из двух основных операций: операция push может использоваться для помещения элементов в стек, а операция pop может удалить верхний элемент из стека.
- Следуйте принципу «последним пришел – первым ушел» (LIFO).
- временная сложность:
- показатель:
O(n)
- поиск:
O(n)
- вставлять:
O(1)
- Удалить:
O(1)
Stack()
Реализация моделирования
public class Stack {
//小贴士:通常可以利用栈实现字符串逆序,还可以利用栈判断分隔符是否匹配,如<a[b{c}]>,可以正进反出,栈为空则成功。
/**基于数组实现的顺序栈,连续存储的线性实现,需要初始化容量**/
//固定数据类型
//private int[] array;
//动态数据类型
private Object[] objArray;
private int maxSize;
private int top;
public Stack() {
}
public Stack(int maxSize) {
if(maxSize > 0){
objArray = new Object[maxSize];
this.maxSize = maxSize;
top = -1;
}else{
throw new RuntimeException("初始化大小错误:maxSize=" + maxSize);
}
}
/**
* 入栈
* @param obj
*/
public void objPush(Object obj){
//扩容
grow();
//++在前表示先运算再赋值,优先级高,在后表示先赋值再运算,优先级低
objArray[++top] = obj;
}
/**
* 出栈
* @return
*/
public Object objPop(){
Object obj = peekTop();
//声明原顶栈可以回收空间(GC)
objArray[top--] = null;
return obj;
}
/**
* 查看栈顶
* @return
*/
public Object peekTop(){
if(top != -1){
return objArray[top];
}else{
throw new RuntimeException("stack is null");
}
}
/**
* 动态扩容
*/
public void grow(){
// << 左移运算符,1表示乘以2的1次方
if(top == maxSize-1){
maxSize = maxSize<<1;
objArray = Arrays.copyOf(objArray,maxSize);
}
}
/**基于链式存储,不连续存储的非线性实现**/
private class Node<Object>{
private Object data;
private Node next;
public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
}
private Node nodeTop;
private int size;
public void nodePush(Object obj){
//栈顶指向新元素,新元素的next指向原栈顶元素
nodeTop = new Node(obj,nodeTop);
size++;
}
public Object nodePop(){
Node old = nodeTop;
//声明原顶栈可以回收空间(GC)
old.next = null;
//栈顶指向下一个元素
nodeTop = nodeTop.next;
size--;
return old.data;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[ ");
for(Node<Object> node = nodeTop; node != null; node = node.next){
sb.append(node.data.toString() + " ");
}
return sb.toString()+"]";
}
public static void main(String[] args) {
// Stack stack = new Stack(1);
// stack.objPush("abc");
// stack.objPush(123);
// stack.objPush("de");
// stack.objPush("cd");
// stack.objPush("er");
// stack.objPush("hello");
// stack.objPush(666);
// stack.objPush(545);
// stack.objPush("word");
// while (stack.top != -1){
// System.out.println(stack.objPop());
// }
// System.out.println(stack.peekTop());
Stack stack = new Stack();
stack.nodePush("111");
stack.nodePush("222");
stack.nodePush("aaa");
stack.nodePush("bbb");
System.out.println(stack);
while (stack.size > 1)
System.out.println(stack.nodePop());
System.out.println(stack);
}
}
Queue
- Очередь — это набор элементов, состоящий из двух основных операций: операция постановки в очередь может использоваться для вставки элементов в очередь, а операция удаления из очереди может использоваться для удаления элементов из очереди.
- Следуйте принципу «первым пришел – первым ушел» (FIFO).
- временная сложность:
- показатель:
O(n)
- поиск:
O(n)
- вставлять:
O(1)
- Удалить:
O(1)
public class Queue {
/***
* 1.单向队列(Queue):只能在一端插入数据,另一端删除数据。
* 2.双向队列(Deque):每一端都可以进行插入数据和删除数据操作。
*
* 与栈不同的是,队列中的数据不总是从数组的0下标开始的
* 选择的做法是移动队头和队尾的指针。
* 为了避免队列不满却不能插入新的数据,我们可以让队尾指针绕回到数组开始的位置,这也称为“循环队列”。
* */
// 单向循环队列,顺序存储结构实现
private Object[] objQueue;
//队列大小
private int maxSize;
//顶部
private int top;
//底部
private int bottom;
//实际元素
private int item;
public Queue(int size) {
maxSize = size;
objQueue = new Object[maxSize];
top = 0;
bottom = -1;
item = 0;
}
/**
* 入队
* @param obj
*/
public void add(Object obj){
if(item == maxSize){
throw new RuntimeException(obj+" add error, queue is full");
}
//循环队列,首尾结合,下标控制队首和队尾位置
if(bottom == maxSize-1){
bottom = -1;
}
objQueue[++bottom] = obj;
item++;
}
/**
* 出对
* @return
*/
public Object out(){
if(item == 0){
throw new RuntimeException("queue is null");
}
Object obj = objQueue[top];
//声明原顶栈可以回收空间(GC)
objQueue[top] = null;
top++;
//重置下标
if(top == maxSize){
top = 0;
}
item--;
return obj;
}
//链式存储结构实现
private class NodeQueue<Object>{
private Object data;
private NodeQueue next;
public NodeQueue(Object data, NodeQueue next) {
this.data = data;
this.next = next;
}
}
//队列头 出
private NodeQueue queueTop;
//队列尾 进
private NodeQueue queueBottom;
//队列大小
private int size;
public Queue() {
queueTop = null;
queueBottom = null;
size = 0;
}
/**
* 入队
* @param obj
*/
public void addNodeQueue(Object obj){
if(size == 0){
queueTop = new NodeQueue(obj,null);
//指向同一存储地址
queueBottom = queueTop;
}else{
NodeQueue<Object> nodeQueue = new NodeQueue(obj,null);
//让尾节点的next指向新增的节点
queueBottom.next = nodeQueue;
//以新节点作为尾节点
queueBottom = nodeQueue;
}
size ++;
}
/**
* 出队
* @return
*/
public Object removeNodeQueue(){
if(size == 0){
throw new RuntimeException("queue is null");
}
NodeQueue nodeQueue = queueTop;
queueTop = queueTop.next;
//声明原队列头next可以回收空间(GC)
nodeQueue.next = null;
size--;
return nodeQueue.data;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("{ ");
for(NodeQueue nodeQueue = queueTop ; nodeQueue != null ; nodeQueue = nodeQueue.next){
sb.append(nodeQueue.data.toString()+" ");
}
return sb.toString()+"}";
}
public static void main(String[] args) {
Queue queue = new Queue();
queue.addNodeQueue("123");
queue.addNodeQueue("abc");
queue.addNodeQueue("ddd");
System.out.println(queue);
queue.removeNodeQueue();
System.out.println(queue);
queue.removeNodeQueue();
queue.removeNodeQueue();
System.out.println(queue);
}
}
Linked List
- Связанный список представляет собой линейный набор узлов, и каждый узел может использовать указатели для указания на другие узлы. Это структура данных, содержащая несколько узлов, которые можно использовать для представления последовательностей.
-
单向链表
: узлы в связанном списке указывают только на следующий узел, а последний узел указывает на ноль. -
双向链表
: где каждый узел имеет два указателя p, n, так что p указывает на предыдущий узел, а n указывает на следующий узел; указатель n последнего узла указывает на ноль. -
循环链表
: связанный список, в котором каждый узел указывает на следующий узел, а последний узел указывает на первый узел. - временная сложность:
- показатель:
O(n)
- поиск:
O(n)
- вставлять:
O(1)
- Удалить:
O(1)
- показатель:
public class LinkedList {
/***
* 链表通常由一连串节点组成,每个节点包含任意的实例数据(data fields)和一或两个用来指向上一个/或下一个节点的位置的链接("links")
*/
private Node head; //链表头
private Node tail; //链表尾
private int size; //节点数
/**
* 双端链表
*/
public class Node{
private Object data;
private Node prev; //上一个
private Node next; //下一个
public Node(Object data) {
this.data = data;
}
}
public LinkedList() {
this.head = null;
this.tail = null;
this.size = 0;
}
/**
* 向链表头添加数据
* @param object
*/
public void addHead(Object object){
Node node = new Node(object);
if(size == 0){
head = node;
tail = node;
size++;
}else{
head.prev = node;
node.next = head;
head = node;
size++;
}
}
/**
* 删除头
*/
public void deleteHead(){
//头部指向下一个,prev值为null则说明是链表的头部
if(size != 0){
head.prev = null;
head = head.next;
size--;
}
}
/**
*向链表尾添加数据
* @param object
*/
public void addTail(Object object){
Node node = new Node(object);
if(size == 0){
head = node;
tail = node;
size++;
}else{
node.prev = tail;
tail.next = node;
tail = node;
size++;
}
}
/**
* 删除尾部
*/
public void deleteTail(){
//尾部指向上一个,next值为null则说明是链表的尾部
if(size != 0){
tail.next = null;
tail = tail.prev;
size--;
}
}
/**
* 显示数据
*/
public void display(){
Node node = head;
while (size > 0){
System.out.print("["+node.data+"->");
node = node.next;
size--;
}
}
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
linkedList.addHead("123");
// linkedList.addHead("abc");
// linkedList.addHead("%?");
// linkedList.addTail("+_+");
// linkedList.addTail("hello");
linkedList.addTail("word");
linkedList.deleteHead();
linkedList.deleteTail();
linkedList.display();
}
}
Binary Tree
Бинарное дерево (состоит из корневого узла и двух непересекающихся левого и правого поддеревьев, называемых корневыми узлами) Двоичное дерево — это древовидная структура данных, в которой каждый узел содержит не более двух узлов: левый дочерний узел и правый дочерний узел.
-
满二叉树
: Каждый узел в дереве содержит только 0 или 2 узла. -
完美二叉树(Perfect Binary Tree)
: Каждый листовой узел в бинарном дереве имеет двух дочерних узлов одинаковой высоты. -
完全二叉树
: За исключением последнего слоя, количество узлов на каждом слое достигает максимального значения, на последнем слое отсутствуют только несколько узлов справа. Красно-черное дерево (пять элементов на узел, цвет (если узел красный, то оба его потомка черные), ключ, левый дочерний элемент, правый байт, родитель)
Heap
- Куча (также известная как приоритетная очередь (очередь + правило сортировки), максимальная куча на рис. 1, минимальная куча на рис. 2)
- Куча — это специальная древовидная структура данных, которая удовлетворяет определенным характеристикам.Значения ключей всех родительских и дочерних узлов во всей куче будут удовлетворять одному и тому же условию сортировки. Куча может быть более точно разделена на максимальную кучу и минимальную кучу.В максимальной куче значение ключа родительского узла всегда больше или равно значению дочернего узла, а максимальное значение во всей куче хранится в корневом узле, а в минимальной куче — родительском. Значение ключа узла всегда меньше или равно значению ключа его дочерних узлов, а минимальное значение во всей куче хранится в корневом узле. .
- временная сложность:
- Доступ макс/мин:
O(1)
- вставлять:
O(log(n))
- Удалить макс/мин:
O(log(n))
- Доступ макс/мин:
Hashing
- Хэш может отображать данные произвольной длины в данные фиксированной длины. Хеш-функция возвращает хеш-значение.Если два разных ключа получают одинаковое хеш-значение, это явление называется коллизией.
- Hash Map: Hash Map — это структура данных, которая может устанавливать отношения между ключами и значениями.Hash Map может использовать хэш-функции для преобразования ключей в индексы в сегментах или слотах, тем самым оптимизируя скорость поиска целевых значений.
- разрешение коллизий
-
链地址法(Separate Chaining)
: в методе цепных адресов каждая корзина независима друг от друга и содержит список ряда индексов. Временная сложность операции поиска представляет собой сумму времени поиска в корзине (фиксированное время) и времени прохождения списка. -
开地址法(Open Addressing)
: В методе открытых адресов, когда вставляется новое значение, будет оцениваться, существует ли хэш-сегмент, соответствующий значению.Если он существует, следующее возможное местоположение будет выбрано по очереди в соответствии с определенным алгоритмом до тех пор, пока не будет незанятого адреса находится. Так называемый метод открытого адреса также означает, что местоположение элемента не всегда определяется его хеш-значением.
-
Graph
- Граф — это абстрактный тип данных, состоящий из отношения «многие ко многим» между элементами данных и набора основных операций.
-
无向图(Undirected Graph)
: неориентированный граф имеет симметричную матрицу смежности, поэтому, если есть ребро от узла u до узла v, также есть ребро от v до u. -
有向图(Directed Graph)
: Матрица смежности ориентированного графа асимметрична, т. е. если есть ребро от u до v, это не означает, что должно быть ребро от v до u.
-
алгоритм
Сортировать
быстрая сортировка
- Стабильный: Нет
- временная сложность:
- Лучшее время:
O(nlog(n))
- Худшее время:
O(n^2)
- Среднее время:
O(nlog(n))
- Лучшее время:
Сортировка слиянием
- Сортировка слиянием — это типичный алгоритм «разделяй и властвуй», который непрерывно делит массив на две части, сортирует левый подмассив и правый подмассив соответственно, а затем объединяет два массива в новый отсортированный массив.
- Стабильный: да
- временная сложность:
- Лучшее время:
O(nlog(n))
- Худшее время:
O(nlog(n))
- Среднее время:
O(nlog(n))
- Лучшее время:
сортировка ведра
- Сегментная сортировка делит массив на конечное число сегментов. Затем каждое ведро сортируется индивидуально (возможно, с использованием другого алгоритма сортировки или рекурсивно продолжает использовать сортировку ведра для сортировки).
- временная сложность:
- Лучшее время:
Ω(n + k)
- Худшее время:
O(n^2)
- Среднее время:
Θ(n + k)
- Лучшее время:
сортировка по основанию
- Поразрядная сортировка похожа на сортировку ведрами, которая делит массив на ограниченное количество ведер, однако не сортирует каждое ведро отдельно после деления, а напрямую объединяет.
- временная сложность:
- Лучшее время:
Ω(nk)
- Худшее время:
O(nk)
- Среднее время:
Θ(nk)
- Лучшее время:
Алгоритмы графов
поиск в глубину
- Алгоритм поиска в глубину — это алгоритм, который сначала обходит дочерние узлы, а не откатывается.
- временная сложность:
O(|V| + |E|)
Поиск в ширину
- Поиск в ширину — это алгоритм обхода графа, который преимущественно обходит соседние узлы, а не дочерние узлы.
- временная сложность:
O(|V| + |E|)
топологическая сортировка
- Топологическая сортировка — это линейная сортировка узлов ориентированного графа.Если существует ребро от u до v, считается, что нижний индекс u находится перед v.
- временная сложность:
O(|V| + |E|)
Алгоритм Дейкстры
- Алгоритм ДейкстрыДля решения задачи поиска кратчайшего пути с одним источником в ориентированных графах.
- временная сложность:
O(|V|^2)
Алгоритм Беллмана-Форда
- **
Bellman-Ford 算法
** — алгоритм расчета кратчайшего пути от одной исходной точки до других узлов взвешенного графа. - Хотя алгоритм сложнее, чем алгоритм Дейкстры, он подходит для графов, содержащих отрицательные ребра.
- временная сложность:
- Лучшее время:
O(|E|)
- Худшее время:
O(|V||E|)
- Лучшее время:
Алгоритм Флойда-Уоршалла
-
Floyd-Warshall 算法
Может использоваться для поиска кратчайшего пути к любому узлу в ациклическом взвешенном графе. - временная сложность:
- Лучшее время:
O(|V|^3)
- Худшее время:
O(|V|^3)
- Среднее время:
O(|V|^3)
- Лучшее время:
Основной алгоритм
- **
Prim 算法
** — жадный алгоритм вычисления минимального остовного дерева во взвешенном неориентированном графе. Другими словами, алгоритм Прима может извлекать подмножество ребер с минимальной стоимостью, соединяющих все узлы в графе. - временная сложность:
O(|V|^2)
Алгоритм Крускала
- **
Kruskal 算法
**Это также алгоритм вычисления минимального остовного дерева графа.Отличие от Prim в том, что граф не должен быть связным. - временная сложность:
O(|E|log|V|)
битовая операция
- Битовая операция — это технология работы на битовом уровне, и правильная битовая операция может помочь нам увеличить скорость работы и уменьшить использование памяти.
- Протестируйте k-й бит:
s & (1 << k)
- Установите k-й бит:
s |= (1 << k)
- k-я позиция равна нулю:
s &= ~(1 << k)
- Переключить значение k-го бита:
s ^= ~(1 << k)
- умножить на 2n:
s << n
- разделить на 2n:
s >> n
- Пересечение:
s & t
- Союз:
s | t
- Вычитание:
s & ~t
- обмен
x = x ^ y ^ (y = x)
- Извлечь младший установленный бит:
s & (-s)
- Извлечь младший неустановленный бит:
~s & (s + 1)
- Значение обмена:
x ^= y; y ^= x; x ^= y;
Эпилог
Продолжение следует....
личный блог~
короткая книга~