考点之数据结构

概论

时间复杂度和空间复杂度是计算机科学中用来评估算法性能的重要指标。

时间复杂度:

时间复杂度衡量的是算法运行所需的时间。它表示算法执行所需的基本操作数量随着输入大小的增长而变化的趋势。

求法:

通常通过分析算法中基本操作执行的次数来计算时间复杂度。这可以通过以下步骤完成:

  1. 识别基本操作:找出算法中执行次数较多或者频繁的基本操作。
  2. 设置算法的输入规模:用一个变量(如n)表示输入的规模。
  3. 建立算法的执行次数表示:用算法中基本操作执行的次数来表示算法的执行时间,通常使用大 O 表示法(比如 O(n)、O(n^2) 等)。
  4. 计算最坏情况:通常分析最坏情况下的时间复杂度,因为它能提供对算法性能上限的评估。

空间复杂度:

空间复杂度是对算法在运行过程中所需的存储空间的度量,也就是算法所使用的内存量。

求法:

计算空间复杂度可以按以下步骤进行:

  1. 计算变量空间:识别算法中使用的变量、数据结构和存储空间,包括输入、输出以及临时空间的需求。
  2. 根据规模设置空间变量:和时间复杂度类似,使用变量(如n)来表示输入规模。
  3. 建立空间需求的表示:基于所用变量和数据结构的空间需求,表示算法的空间消耗,通常也使用大 O 表示法。

这些复杂度指标对于评估算法的效率和性能至关重要。优化算法以降低时间复杂度和空间复杂度是算法设计中的核心目标。

这里似乎有一些关于链表和顺序表的基本操作以及待完成的任务列表。首先,我会解释一些与链表和顺序表相关的操作:

顺序表(Array List):

顺序表是一种基于数组实现的线性表,它按照元素在内存中的相邻顺序进行存储。顺序表的特点是可以快速地通过下标直接访问元素,但在插入或删除元素时可能需要移动其他元素,导致效率低下。

链表(Linked List):

链表是一种基于节点的数据结构,它包含一个指向下一个节点的指针。主要有单链表和双链表两种形式。

单链表(Singly Linked List):

单链表中的每个节点包含一个指向下一个节点的指针,最后一个节点指向空值。删除单链表中相同值的节点通常需要遍历链表,判断节点值并删除。

双链表(Doubly Linked List):

双链表中的节点包含两个指针,分别指向前一个节点和后一个节点。循环双向链表是一种特殊的双链表,其尾节点指向头节点,形成一个闭环。

对于链表中的待完成任务:

  • 逆置(Reverse):将链表中的节点顺序颠倒。
  • 最值(Maximum/Minimum):在链表中查找最大或最小值的节点。
  • 划分(Partition):按照特定条件(如某个值)划分链表。
  • 归并(Merge):合并两个有序链表。

头插法(Head Insertion):

在链表中插入元素时,将新节点插入到链表的头部。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

尾插法(Tail Insertion):

在链表中插入元素时,将新节点插入到链表的尾部。

以上提到的操作都是链表和顺序表中常见的操作,可以根据具体情况实现相应的算法和代码。对于链表的每个任务,需要编写相应的算法来实现所需的操作。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 整体代码
#include "stdio.h"
#include "stdlib.h"typedef int ElemType;
typedef struct LNode{ElemType data;           //数据域struct LNode *next;      //指针域
}LNode,*LinkList;/** 头插法 有头结点*/
LinkList HeadInster(LinkList &L,int n){LNode *s;int x=1;L= (LinkList)malloc(sizeof(LNode));     //创建头结点L->next=NULL;                                //初始为空链表while(x!=n){s=(LNode*) malloc(sizeof(LNode));   //创建新结点s->data=x;s->next=L->next;L->next=s;x++;}return L;
}/** 头插法 无头结点*/
LinkList Headinster(LinkList &L,int n){LNode *s;int x=1;L= (LinkList)malloc(sizeof(LNode));L->data=x++;L->next=NULL;while(x!=n){s=(LNode*) malloc(sizeof(LNode));s->data=x;s->next=L;L=s;x++;}return L;
}/** 尾插法、有结点*/
LinkList TailInster(LinkList &L,int n){int x=1;L= (LinkList)malloc(sizeof(LNode));LNode *s,*r=L;while(x!=n){s=(LNode*) malloc(sizeof(LNode));s->data=x;r->next=s;r=s;x++;}r->next=NULL;return L;
}
/** 尾插法、无结点*/
LinkList Tailinster(LinkList &L,int n){int x=1;L= (LinkList)malloc(sizeof(LNode));L->data=x++;LNode *s,*r=L;while(x!=n){s=(LNode*) malloc(sizeof(LNode));s->data=x;r->next=s;r=s;x++;}r->next=NULL;return L;
}/** 便利链表、头结点*/
void PrintList(LinkList L){LNode *s;s=L->next;while (s!=NULL) {printf("%d\t",s->data);s=s->next;}
}/** 便利链表*/
void Print(LinkList L){LNode *s;s=L;while (s!=NULL) {printf("%d\t",s->data);s=s->next;}
}int main(){LinkList L,S,P,Q;printf("有头结点的头插法:");HeadInster(L,10);PrintList(L);printf("\n无头结点的头插法:");Headinster(P,10);Print(P);printf("\n有头结点的尾插法:");Tailinster(S,10);Print(S);printf("\n无头结点的尾插法:");Tailinster(Q,10);Print(Q);}

实现查找某一个位置的元素

顺序栈基本操作:

压栈(Push):将元素放入栈顶。即,在栈顶添加一个元素。
出栈(Pop):从栈顶移除元素。即,移除栈顶的元素并返回它。
插入(Insert):顺序栈通常只允许在栈顶进行插入操作,即压栈操作。
删除(Delete):顺序栈中删除操作一般指出栈操作,移除栈顶元素。
查找(Search):可以查找栈中特定元素的位置或者判断栈中是否存在某个元素。
这些操作的实现基于顺序栈的特性,可以使用数组来实现栈的这些功能。以下是伪代码表示顺序栈的基本操作:

数据结构顺序栈:初始化栈(初始化数组大小、栈顶指针等)push(元素):如果栈已满:返回栈已满否则:将元素放入栈顶栈顶指针加一pop():如果栈为空:返回栈为空否则:弹出栈顶元素栈顶指针减一insert(元素):与push操作相同,向栈中压入元素delete():与pop操作相同,从栈中移除栈顶元素search(元素):从栈顶开始遍历栈中元素:如果找到指定元素:返回该元素在栈中的位置(或者True)如果遍历结束未找到元素:返回未找到该元素(或者False)

代码:

#include <stdio.h>
#include <stdbool.h>#define MAX_SIZE 5struct SequenceStack {int stack[MAX_SIZE];int top;
};// 初始化栈
void initStack(struct SequenceStack *s) {s->top = -1; // 初始化栈顶指针
}// 判断栈是否为空
bool isEmpty(struct SequenceStack *s) {return s->top == -1;
}// 判断栈是否已满
bool isFull(struct SequenceStack *s) {return s->top == MAX_SIZE - 1;
}// 元素压栈
void push(struct SequenceStack *s, int element) {if (isFull(s)) {printf("Stack is full. Cannot push element.\n");return;}s->top++;s->stack[s->top] = element;
}// 元素出栈
int pop(struct SequenceStack *s) {if (isEmpty(s)) {printf("Stack is empty. Cannot pop element.\n");return -1;}int element = s->stack[s->top];s->top--;return element;
}// 元素插入(与压栈操作相同)
void insert(struct SequenceStack *s, int element) {push(s, element);
}// 删除元素(与出栈操作相同)
int delete(struct SequenceStack *s) {return pop(s);
}// 查找元素在栈中的位置
int search(struct SequenceStack *s, int element) {for (int i = s->top; i >= 0; i--) {if (s->stack[i] == element) {return i; // 返回元素在栈中的位置}}return -1; // 元素未找到
}int main() {struct SequenceStack stack;initStack(&stack); // 初始化栈push(&stack, 10);push(&stack, 20);push(&stack, 30);printf("Stack after pushing elements: ");for (int i = 0; i <= stack.top; i++) {printf("%d ", stack.stack[i]);}printf("\n");printf("Popped element: %d\n", pop(&stack));printf("Stack after popping element: ");for (int i = 0; i <= stack.top; i++) {printf("%d ", stack.stack[i]);}printf("\n");int position = search(&stack, 20);if (position != -1) {printf("Element 20 found at position %d in the stack.\n", position);} else {printf("Element 20 not found in the stack.\n");}return 0;
}

链栈

  • 链栈基本操作:
    压栈(Push):将元素放入栈顶。在链栈中,通常指的是在链表的头部添加一个新节点。
    出栈(Pop):从栈顶移除元素。在链栈中,即删除链表的头节点,并返回其值。
    栈内容转置:将链栈中的内容顺序颠倒,可以利用辅助栈或指针进行转置操作。
    入栈顺序、出栈顺序:通过尝试不同的出栈顺序来计算入栈顺序有几种、判断是否合法。
    以下是链栈的相关操作实现以及入栈、出栈顺序检验的基本思路:

链栈基本操作的 C 语言实现:

#include <stdio.h>
#include <stdlib.h>// 链表节点定义
struct Node {int data;struct Node* next;
};// 链栈定义
struct LinkedStack {struct Node* top;
};// 初始化栈
void initStack(struct LinkedStack* stack) {stack->top = NULL; // 栈顶初始化为空
}// 判断栈是否为空
int isEmpty(struct LinkedStack* stack) {return (stack->top == NULL); // 若栈顶为空则栈为空
}// 压栈操作
void push(struct LinkedStack* stack, int value) {struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); // 创建新节点newNode->data = value;newNode->next = stack->top; // 新节点作为栈顶stack->top = newNode;
}// 出栈操作
int pop(struct LinkedStack* stack) {if (isEmpty(stack)) {printf("Stack is empty. Cannot pop element.\n");return -1;}int value = stack->top->data;struct Node* temp = stack->top;stack->top = stack->top->next; // 栈顶指针移至下一个节点free(temp); // 释放原栈顶节点return value;
}// 栈内容转置
void reverseStack(struct LinkedStack* stack) {struct LinkedStack tempStack;initStack(&tempStack); // 初始化临时栈while (!isEmpty(stack)) {int value = pop(stack); // 从原栈弹出元素push(&tempStack, value); // 推入临时栈}stack->top = tempStack.top; // 将临时栈内容赋给原栈
}int main() {struct LinkedStack stack;initStack(&stack); // 初始化栈// 入栈push(&stack, 10);push(&stack, 20);push(&stack, 30);// 显示转置前的栈内容printf("Stack content before reversal: ");while (!isEmpty(&stack)) {printf("%d ", pop(&stack));}printf("\n");// 重新入栈push(&stack, 10);push(&stack, 20);push(&stack, 30);// 栈内容转置reverseStack(&stack);// 显示转置后的栈内容printf("Stack content after reversal: ");while (!isEmpty(&stack)) {printf("%d ", pop(&stack));}printf("\n");return 0;
}

队列

循环队列是一种基于数组实现的队列,通过循环利用数组空间来实现队列的基本操作。求解循环队列中元素个数的方法很简单,可以通过队头和队尾指针的位置关系来进行计算。

循环队列的基本操作:

  1. 初始化:设置队列的大小,队头、队尾指针等。
  2. 入队:将元素添加到队列尾部。
  3. 出队:从队列头部移除元素。
  4. 判空:判断队列是否为空。
  5. 判满:判断队列是否已满。
  6. 求元素个数:计算队列中当前元素的个数。

以下是一个 C 语言实现的循环队列示例代码,并计算队列中元素的个数:

#include <stdio.h>
#include <stdbool.h>#define MAX_SIZE 5struct CircularQueue {int items[MAX_SIZE];int front, rear;int count; // 用来统计元素个数
};// 初始化队列
void initQueue(struct CircularQueue* queue) {queue->front = 0;queue->rear = -1;queue->count = 0;
}// 判空
bool isEmpty(struct CircularQueue* queue) {return (queue->count == 0);
}// 判满
bool isFull(struct CircularQueue* queue) {return (queue->count == MAX_SIZE);
}// 入队
void enqueue(struct CircularQueue* queue, int value) {if (!isFull(queue)) {queue->rear = (queue->rear + 1) % MAX_SIZE;queue->items[queue->rear] = value;queue->count++;} else {printf("Queue is full. Cannot enqueue element.\n");}
}// 出队
int dequeue(struct CircularQueue* queue) {if (!isEmpty(queue)) {int dequeuedItem = queue->items[queue->front];queue->front = (queue->front + 1) % MAX_SIZE;queue->count--;return dequeuedItem;} else {printf("Queue is empty. Cannot dequeue element.\n");return -1;}
}// 计算队列中元素个数
int countElements(struct CircularQueue* queue) {return queue->count;
}int main() {struct CircularQueue queue;initQueue(&queue);enqueue(&queue, 10);enqueue(&queue, 20);enqueue(&queue, 30);enqueue(&queue, 40);enqueue(&queue, 50);printf("Number of elements in the queue: %d\n", countElements(&queue));return 0;
}

这段代码实现了一个循环队列,并通过 countElements() 函数计算了队列中元素的个数。在 main() 函数中,演示了向队列中添加元素后如何计算队列中的元素个数。

串(顺序存储)

顺序存储的串是一种由字符数组构成的字符串,其中每个字符按照特定顺序依次排列。在顺序存储的串中,可以进行插入、删除、查找等操作,并且可以实现串的比较、统计每个元素出现的次数以及区分空串和空格串。

插入、删除、查找

  • 插入:在串的任意位置插入一个字符或者另一个串。
  • 删除:删除串中指定位置的字符或一段字符。
  • 查找:查找串中特定的子串或字符。

串的比较

串的比较是指比较两个串的大小关系,可以根据字典序等规则来进行比较。

每个元素出现次数

统计每个元素在串中出现的次数,遍历串并计数即可实现。

空串和空格串的区别

  • 空串:指的是一个没有任何字符的串,长度为0。
  • 空格串:指的是串中只包含空格字符(ASCII码为32)的串,长度大于等于1。

下面是一个 C 语言示例,展示了如何进行这些操作:

#include <stdio.h>
#include <string.h>// 插入字符
void insertChar(char str[], char ch, int position) {memmove(&str[position + 1], &str[position], strlen(str) - position + 1);str[position] = ch;
}// 删除字符
void deleteChar(char str[], int position) {memmove(&str[position], &str[position + 1], strlen(str) - position);
}// 查找字符
int findChar(char str[], char ch) {char *ptr = strchr(str, ch);if (ptr != NULL) {return ptr - str; // 返回找到的位置} else {return -1; // 没找到返回-1}
}// 串的比较
int compareStrings(char str1[], char str2[]) {return strcmp(str1, str2);
}// 统计每个元素出现次数
void countOccurrences(char str[]) {int count[256] = {0}; // 256个ASCII字符,初始化计数为0for (int i = 0; str[i] != '\0'; i++) {count[(int)str[i]]++; // 统计字符出现次数}for (int i = 0; i < 256; i++) {if (count[i] > 0) {printf("Character '%c' occurs %d times.\n", i, count[i]);}}
}int main() {char str[] = "Hello World";insertChar(str, 'X', 3); // 在位置3插入字符Xprintf("After insertion: %s\n", str);deleteChar(str, 7); // 删除位置7的字符printf("After deletion: %s\n", str);int position = findChar(str, 'W'); // 查找字符W的位置if (position != -1) {printf("Character found at position %d\n", position);} else {printf("Character not found\n");}char str1[] = "Hello";char str2[] = "World";int comparison = compareStrings(str1, str2);if (comparison == 0) {printf("The strings are equal\n");} else if (comparison < 0) {printf("String 1 is less than string 2\n");} else {printf("String 1 is greater than string 2\n");}countOccurrences(str); // 统计字符出现次数return 0;
}

这个示例程序包含了插入、删除、查找字符,串的比较,以及统计每个元素出现次数的操作。你可以根据需要进行调整和扩展。

数组

二维数组的地址运算

在 C 语言中,二维数组在内存中是按行存储的。数组名代表数组的起始地址,而对于二维数组,可以通过下标访问元素。地址运算可以使用指针来完成,计算特定元素的地址。

假设有一个二维数组 arr[row][col]

  • 访问元素 arr[i][j] 的地址:&arr[i][j](arr + i * col + j)

对称矩阵的地址运算

对称矩阵是一个方阵,其下三角或上三角对角线对称。这意味着实际上只需要存储其中一半的元素。当以一维数组表示时,元素的排列方式决定了地址的计算。

如果将对称矩阵存储在一维数组中:

  • 访问元素 arr[i][j] 的地址:将二维坐标 (i, j) 映射到一维数组的地址。

稀疏矩阵的表示方法

稀疏矩阵是大部分元素为0的矩阵。稀疏矩阵常用三元组表或十字链表来表示。

  • 三元组表:记录非零元素的行、列以及元素值。
  • 十字链表:将非零元素的行、列和值存储在节点中,并使用链表连接。

对于三元组表的转置,只需要交换行和列的位置即可。

三对角阵

三对角矩阵是一种特殊的矩阵,除主对角线外,只有相邻对角线和主对角线上的元素可能非零。在一维数组中存储时,通常以紧凑形式存储,即仅存储主对角线和两个相邻对角线上的元素。

  • 访问元素 arr[i][j] 的地址:将二维坐标 (i, j) 映射到一维数组的地址。

特殊矩阵在一维数组中存储

特殊矩阵(如对称矩阵、三对角矩阵等)在一维数组中存储时,可以利用数学性质和矩阵的结构,减少存储空间并优化访问速度。

以下是一个 C 语言的示例,展示了对称矩阵和三对角矩阵在一维数组中存储的方式:

#include <stdio.h>// 访问对称矩阵元素的地址
int getAddressSymmetric(int row, int col, int n) {if (row >= col) {return (row * (row + 1) / 2) + col;} else {return (col * (col + 1) / 2) + row;}
}// 访问三对角矩阵元素的地址
int getAddressTridiagonal(int row, int col, int n) {int diff = row - col;if (diff == 0) {return row;} else if (diff == 1) {return n + row - 1;} else if (diff == -1) {return 2 * n + row - 1;} else {return -1; // 非三对角元素,根据需求自行扩展}
}int main() {int n = 5; // 矩阵大小int symmetric[n * (n + 1) / 2];int tridiagonal[3 * n - 2];int row = 2, col = 3;int addressSymmetric = getAddressSymmetric(row, col, n);int addressTridiagonal = getAddressTridiagonal(row, col, n);printf("Address in symmetric matrix: %d\n", addressSymmetric);printf("Address in tridiagonal matrix: %d\n", addressTridiagonal);return 0;
}

这个示例程序展示了如何计算对称矩阵和三对角矩阵在一维数组中的地址。你可以根据需求扩展这些函数以适应不同类型的特殊矩阵。

二叉树

1. 性质
  • 二叉树是由节点组成的树状结构,每个节点最多有两个子节点,分别为左子节点和右子节点。
  • 左子节点小于或等于父节点,右子节点大于父节点的二叉树称为二叉搜索树。
  • 遍历二叉树有三种基本顺序:前序(根-左-右)、中序(左-根-右)、后序(左-右-根)。
  • 完全二叉树是一个除了最底层外所有层都是满的,并且最底层的节点尽可能地集中在树的左边的二叉树。
2. 遍历
  • 前序遍历:根-左-右
  • 中序遍历:左-根-右
  • 后序遍历:左-右-根
  • 层次遍历:逐层从左到右遍历
3. 线索二叉树
  • 线索二叉树是为了加速遍历而提出的,它的每个节点指向其前驱和后继节点。通过线索化,可以不使用递归或栈来进行遍历。
4. 二叉排序树
  • 二叉排序树(BST)是一种特殊的二叉树,其中每个节点的左子树都小于节点的值,右子树都大于节点的值。
  • 插入:按照大小顺序找到合适的位置插入新节点。
  • 删除:根据情况考虑删除节点:若无子节点直接删除,若有一个子节点则用其子节点替代,若有两个子节点则选择左子树最大节点或右子树最小节点替代删除节点。
5. 完全二叉树
  • 完全二叉树是一种特殊的二叉树,除了最底层,其他层都是满的,并且最底层的节点尽可能地集中在树的左边。
  • 层次遍历是一个实现完全二叉树的有效方法。
6. 满二叉树
  • 满二叉树是一种特殊的二叉树,每一层的节点数都达到最大值。
7. 哈夫曼树
  • 哈夫曼树是一种带权路径长度最短的二叉树。通过构建哈夫曼树,可以实现数据的最优编码。

画出哈夫曼树

    Example:30/  \10   20/ \   / \5  3  7  7

编程实现

哈夫曼树的实现通常需要建立优先队列,并进行树的合并和构建。以下是一个简单的示例:

#include <stdio.h>
#include <stdlib.h>struct Node {int data;struct Node *left, *right;
};struct Node* createNode(int data) {struct Node* node = (struct Node*)malloc(sizeof(struct Node));node->data = data;node->left = node->right = NULL;return node;
}int main() {struct Node *root = createNode(30);root->left = createNode(10);root->right = createNode(20);root->left->left = createNode(5);root->left->right = createNode(3);root->right->left = createNode(7);root->right->right = createNode(7);return 0;
}

这段代码创建了一个简单的哈夫曼树结构。实际的哈夫曼编码需要根据数据频率动态构建树,并实现编码和解码操作。

图相关算法和表示方法

1. 遍历(DFS、BFS)
  • 深度优先搜索 (DFS):沿着图的深度遍历,尽可能深地搜索图的分支。
  • 广度优先搜索 (BFS):逐层访问邻居节点,先访问离起始节点最近的节点。
2. 最小生成树(prim、kruskal)
  • Prim 算法:从一个顶点开始,每次选择一条权值最小的边来连接到新的顶点,直到覆盖所有的顶点。
  • Kruskal 算法:按边的权值从小到大顺序选择边,若加入该边不产生回路,则加入,直到覆盖所有的顶点。
3. 最短路径(Dijkstra、Floyd)
  • Dijkstra 算法:基于贪心策略,逐步扩展路径,找出起点到其他顶点的最短路径。
  • Floyd 算法:通过动态规划,逐步更新最短路径,计算图中任意两点之间的最短路径。
4. 关键路径
  • 关键路径:在一个有向图中,表示从起始节点到结束节点的最长路径,决定了整个图的时间长度。
5. 拓扑排序
  • 拓扑排序:针对有向无环图,通过对顶点排序使得图中的每条有向边从低序顶点指向高序顶点。
6. 邻接表/邻接矩阵表示图
  • 邻接表:使用链表存储图的顶点和相邻顶点的关系。
  • 邻接矩阵:使用二维数组存储图的顶点和边的关系。
7. 深度优先遍历序列
  • 深度优先遍历序列:记录顶点被访问的先后顺序。
8. 稀疏矩阵的十字链表表示法
  • 十字链表:一种表示稀疏矩阵的方法,结合链表结构实现矩阵的压缩存储。使用两类链表,分别表示非零元素和维度信息。

以下是一个 C 语言示例,展示了稀疏矩阵的十字链表表示法:

检索

线性表检索是在线性结构数据中进行搜索特定元素的过程。常见的线性表检索方法包括顺序查找、二分查找和分块查找。

1. 顺序查找(Sequential Search)

  • 顺序查找是最简单直接的查找方法。它从线性表的第一个元素开始逐个扫描,直到找到目标元素或者遍历整个表。
顺序查找的步骤:
  1. 从表的第一个元素开始,逐个和目标元素进行比较。
  2. 如果找到目标元素,返回元素的位置;否则,返回未找到的标识。

2. 二分查找(Binary Search)

  • 二分查找是在有序数组中进行查找的有效方法。它通过将目标值和有序数组的中间值进行比较,并据此决定将搜索范围缩小为前半部分或后半部分。
二分查找的步骤:
  1. 确保数组是有序的。
  2. 确定搜索范围的左右边界。
  3. 计算中间元素的索引。
  4. 将目标值与中间值比较。
  5. 如果目标值大于中间值,缩小搜索范围至右半部分;否则,缩小至左半部分。
  6. 重复这个过程直至找到目标值或搜索范围缩小为 0。

3. 分块查找(Block Search)

  • 分块查找是对元素进行分块,然后在块内进行顺序查找,同时查找块的索引,最终确定元素的位置。
分块查找的步骤:
  1. 将线性表分成若干块,每个块内的元素可以是无序的。
  2. 建立一个块的索引表,记录每块的起始位置和最大值。
  3. 通过查找块索引表找到目标元素应该在的块。
  4. 在该块内进行顺序查找。

这些检索方法在不同情况下有着不同的适用性。例如,顺序查找适用于未排序或少量元素的线性表,而二分查找适用于已排序的线性表。分块查找结合了两者的优点,在大规模数据的情况下可以减少搜索的时间复杂度。

你可以根据数据的特性和检索的要求选择合适的检索算法。

散列表

散列表(Hash Table)是一种数据结构,旨在实现快速的数据检索。它通过哈希函数将关键字映射到哈希表中的位置。不同的冲突解决方法,例如线性探测、二次探测以及拉链法,用于处理不同的哈希冲突。

如何建立一个表

1. 哈希函数
  • 哈希函数将关键字映射到哈希表中的位置。
  • 关键是设计一个均匀、不易产生冲突的哈希函数。
2. 不同的冲突解决方法
a. 线性探测
  • 线性探测是一种解决冲突的方法。如果哈希函数产生了冲突,它会在哈希表中的下一个位置继续寻找空槽。
  • 冲突解决公式为 ( h(k, i) = (h’(k) + i) \mod m ),其中 (h’(k)) 是哈希函数,(i) 是探测序列,(m) 是表的大小。
b. 二次探测
  • 二次探测是线性探测的改进,它采用二次方程进行步长探测,以避免线性探测可能出现的线性探测失效的问题。
  • 冲突解决公式为 ( h(k, i) = (h’(k) + c_1 \cdot i + c_2 \cdot i^2) \mod m ),其中 (c_1, c_2) 是常数。
c. 拉链法(Chaining)
  • 拉链法使用链表解决冲突。当哈希函数产生冲突时,将具有相同哈希值的元素存储在同一位置的链表中。

开放地址法和链地址法求平均查找长度(Average Search Length, ASL)

开放地址法
  • ASL 是在散列表中查找元素时所需的平均操作数。
  • 对于开放地址法,ASL 取决于装填因子(load factor)和冲突解决方法。
链地址法
  • 链地址法中的 ASL 通常取决于装填因子和链表长度的平均值。

ASL 的公式可以根据不同的冲突解决方法和装填因子来计算。例如,对于线性探测和二次探测,ASL 的计算与冲突的发生和解决次数相关。对于拉链法,ASL 取决于链表长度的平均值。

这些方法的选择取决于实际应用需求和数据特征。例如,对于大型数据集,装填因子要小,而对于小型数据集,可以选择拉链法等方法。

内排序

内排序算法

内排序是对存储在内存中的数据进行排序的过程。它涉及的数据量通常不超过计算机的内存容量。

1. 冒泡排序(Bubble Sort)
  • 冒泡排序是一种简单的排序算法,它重复地走访要排序的数列,依次比较相邻的两个元素,将较大的元素交换到右侧。
  • 时间复杂度:平均情况和最坏情况都为 O(n^2)。
2. 快速排序(Quick Sort)
  • 快速排序采用分治策略,选择一个基准值,将小于基准值的元素放在左侧,大于基准值的元素放在右侧。然后递归地对左右两部分进行排序。
  • 时间复杂度:平均情况下为 O(n log n),最坏情况下为 O(n^2)。
3. 基数排序(Radix Sort)
  • 基数排序是一种非比较型的排序算法,它按照低位先排序,然后收集,再按照高位排序,再收集,以此类推,直到高位。
  • 时间复杂度:O(k * n),其中 k 为数字位数。
4. 堆排序(Heap Sort)
  • 堆排序利用堆的性质进行排序。它将待排序的序列构建成一个大顶堆(或小顶堆),依次取堆顶元素,然后重新调整堆,直到排序完成。
  • 时间复杂度:平均、最坏和最好情况下都为 O(n log n)。
5. Shell排序(Shell Sort)
  • Shell排序是一种插入排序的变体,它通过比较相距一定间隔的元素来工作,使离正确位置很远的元素能快速回到合适位置。
  • 时间复杂度:取决于选取的增量序列,平均情况下为 O(n log n)。

这些算法在不同场景下有着不同的效率表现,适合处理不同规模和类型的数据。冒泡和快速排序都属于交换排序,而基数排序、堆排序和Shell排序则是其他类型的排序算法。

你可以根据数据的规模和特征,选择最适合的排序算法来进行排序。

数据结构和数据类型

数据结构和数据类型是计算机科学中两个相关但不同的概念。

数据结构是一种组织和存储数据的方式,它定义了数据元素之间的关系以及对这些数据元素进行操作的规则。数据结构可以是线性的,如数组、链表等,也可以是非线性的,如树、图等。它们用于存储和处理数据,以便在计算机程序中执行各种操作,例如查找、排序、插入、删除等。

数据类型则是一种编程语言的特性,它定义了数据的性质和数据可以执行的操作。数据类型规定了数据的存储方式、范围和可执行的操作。常见的数据类型包括整数、浮点数、字符、布尔值等。编程语言使用数据类型来确保数据的正确性和一致性,以便在程序中进行类型检查和运算。

总结:

数据结构是用于组织和存储数据的方式,它涉及数据元素之间的关系和操作规则。
数据类型是编程语言中的概念,它规定了数据的性质和操作规则。
数据结构通常依赖于数据类型来存储和操作数据。
数据类型是编程语言的一部分,而数据结构是程序设计的一部分。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/123570.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

国际阿里云CDN加速OSS资源教程!

当您需要加速OSS上的静态资源时&#xff0c;可以通过阿里云CDN加速OSS域名&#xff0c;实现静态资源的访问加速。本文详细介绍了通过CDN控制台实现OSS加速的操作流程和应用场景。 客户价值 阿里云OSS可提供低成本的存储&#xff0c;CDN可以实现静态资源加速分发。使用OSS作为C…

ESM蛋白质语言模型系列

模型总览 第一篇《Biological structure and function emerge from scaling unsupervised learning to 250 million protein sequences 》ESM-1b 第二篇《MSA Transformer》在ESM-1b的基础上作出改进&#xff0c;将模型的输入从单一蛋白质序列改为MSA矩阵&#xff0c;并在Tran…

使用设计模式基于easypoi优雅的设计通用excel导入功能

文章目录 概要整体架构流程代码设计配置类通用API分发器处理器业务逻辑处理service接口策略模型 小结 概要 基于java原生 easypoi结合适配器模式、策略模式、工厂模式设计一个通用的excel导入框架 整体架构流程 代码设计 由上到下&#xff0c;分别讲解代码 配置类 ExcelCon…

Go学习第十六章——Gin文件上传与下载

Go web框架——Gin文件上传与下载 1. 文件上传1.1 入门案例&#xff08;单文件&#xff09;1.2 服务端保存文件的几种方式SaveUploadedFileCreateCopy 1.3 读取上传的文件1.4 多文件上传 2. 文件下载2.1 快速入门2.2 前后端模式下的文件下载2.3 中文乱码问题 1. 文件上传 1.1 …

计算机毕业设计选题推荐-周边美食推荐微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

kafka3.X基本概念和使用

kafka基本概念和使用 文章目录 kafka基本概念和使用 kafka的概念基本概念Kafka的使用 首先kafka的安装kafka的简单实用和理解搭建集群&#xff08;3个节点&#xff09;windows版本环境搭建 本文"kafka的概念"部分是在[初谈Kafka][ https://juejin.im/post/5a8e7f…

VulnHub DC-1

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

微信小程序 - 页面继承(非完美解决方案)

微信小程序 - 面页继承&#xff08;非完美解决方案&#xff09; 废话思路首页 indexindex.jsindex.jsonindex.wxml 父页面 page-basepage-base.jspage-base.wxml 子页面 page-apage-a.jspage-a.wxml 子页面 page-bpage-b.jspage-b.wxml 其它app.jsapp.jsonapp.wxss 参考资料 废…

BI是什么?想要了解BI需要从哪些方面入手?

企业为了执行数字化战略&#xff0c;实行数字化转型&#xff0c;实现数据价值&#xff0c;除了需要相关数字化技术及理念、人才等&#xff0c;还需要借助数字化相关应用&#xff0c;例如商业世界中广受企业欢迎的ERP、OA、CRM等业务信息系统&#xff0c;以及上升势头非常迅猛的…

【STM32】STM32中断体系

一、STM32的NVIC和起始代码中的ISP 1.NVIC(嵌套向量中断控制器) (1)数据手册中相关部分浏览 (2)地址映射时0地址映射到Flash或SRAM (3)中断向量表可以被人为重新映射&#xff0c;一般用来IAP中 (4)STM32采用一维的中断向量表 (5)中断优先级设置有点复杂&#xff0c;后面细说 1…

【排序算法】 归并排序详解!分治思想!

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; 算法—排序篇 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言&#x1f324;️归并排序的思想☁️基本思想☁️归并的思想实现☁️分治法 &#x1f3…

IOC课程整理-20 Spring 应用上下文生命周期

0.目录 1. Spring 应用上下文启动准备阶段 2. BeanFactory 创建阶段 3. BeanFactory 准备阶段 4. BeanFactory 后置处理阶段 5. BeanFactory 注册 BeanPostProcessor 阶段 6. 初始化內建 Bean&#xff1a;MessageSource 7. 初始化內建 Bean&#xff1a;Spring 事件广播器…

计算机毕业设计选题推荐-戏曲文化苑微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

Springboot 使用JavaMailSender发送邮件 + Excel附件

目录 1.生成Excel表格 1.依赖设置 2.代码&#xff1a; 2.邮件发送 1.邮件发送功能实现-带附件 2.踩过的坑 1.附件名中文乱码问题 3.参考文章&#xff1a; 需求描述&#xff1a;项目审批完毕后&#xff0c;需要发送邮件通知相关人员&#xff0c;并且要附带数据库表生成的…

Linux C/C++ 实现网络流量分析(性能工具)

网络流量分析的原理基于对数据包的捕获、解析和统计分析&#xff0c;通过对网络流量的细致观察和分析&#xff0c;帮助管理员了解和优化网络的性能、提高网络安全性&#xff0c;并快速排查和解决网络故障和问题。 Linux中的网络流量常见类型 在Linux中&#xff0c;网络流量可以…

校园物业报修小程序开发笔记一

背景 校园规模和复杂性&#xff1a; 大型学校和校园通常拥有众多的建筑物、设施和设备&#xff0c;需要有效的维护和报修系统&#xff0c;以满足学生、教职员工和校园管理人员的需求。 学生和员工需求&#xff1a; 学生和员工在校园内可能遇到各种维修问题&#xff0c;如故障的…

ESP8266,手机与电脑之间的TCP通讯

电脑端运行通讯猫调试助手,作为服务端: 电脑端 电脑的IP地址是: 192.168.2.232 手机与电脑之间的TCP通讯 手机端运行网络调试精灵,作为客户端: 手机端 如果从手机端点击"发送"按钮,则也会将"ghhh东方红广场"几个字发送到电脑上(服务端). ESP8266作为客户…

elasticsearch一些重要的配置参数

先看一下官网给我们提供的全部的参数配置项 官网地址 官方文档链接&#xff1a;注意版本是8.1Configuring Elasticsearch | Elasticsearch Guide [8.1] | Elastic​编辑https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html 重要&#xff08;基本…

【2023Mathorcup大数据】B题 电商零售商家需求预测及库存优化问题 python代码解析

【2023Mathorcup大数据】B题 电商零售商家需求预测及库存优化问题 python代码解析 1 题目 2023 年MathorCup 高校数学建模挑战赛——大数据竞赛赛道B&#xff1a;电商零售商家需求预测及库存优化问题电商平台存在着上千个商家&#xff0c;他们会将商品货物放在电商配套的仓库…

通道洗牌的思想神了

大家好啊&#xff0c;我是董董灿。 昨天写了一篇关于分组卷积的文章&#xff1a;分组卷积的思想神了&#xff0c;然后有同学希望多了解下通道洗牌。 我个人感觉&#xff0c;通道洗牌这个算法&#xff0c;或者说这个思想&#xff0c;可以称之为小而精&#xff0c;并且是实际解…