赞
踩
常见的数据结构包括数组、链表、栈、队列、哈希表、树、堆、图,它们从 逻辑结构 和 物理结构 两个维度进行分类。
⚪ 逻辑结构揭示了数据元素之间的逻辑关系:
一对一
的顺序关系。一对多
的关系。多对多
的关系。⚪ 物理结构反映了数据在计算机内存中的存储方式:
内存
中。基于数组、链表
或二者的组合实现的。基本数据类型:
是 CPU 可以直接进行运算的类型,在算法中直接被使用,主要包括以下几种。
基本数据类型以二进制的形式存储在计算机中。
基本数据类型提供了数据的 内容类型
,而数据结构提供了数据的 组织方式
。
数组(array)
是一种线性数据结构,其将相同类型的元素存储在连续的内存空间中。元素在数组中的位置称为该元素的索引(index)
。
1:初始化数组
2:访问元素
3:插入元素
4:删除元素
5:遍历数组
6:查找元素
7:扩容数组
代码示例:
/*1------ 初始化数组 */ int arr[5]; int nums[5] = { 1, 3, 2, 5, 4 };// 存储在栈上 int* arr1 = new int[5];// 存储在堆上(需要手动释放空间) int* nums1 = new int[5] { 1, 3, 2, 5, 4 }; /*2------ 随机访问元素 */ int randomAccess(int *nums, int size) { int randomIndex = rand() % size;// 在区间 [0, size) 中随机抽取一个数字 int randomNum = nums[randomIndex];// 获取并返回随机元素 return randomNum; } /*3------ 在数组的索引 index 处插入元素 num */ void insert(int *nums, int size, int num, int index) { for (int i = size - 1; i > index; i--) {// 把索引 index 以及之后的所有元素向后移动一位 nums[i] = nums[i - 1]; } nums[index] = num;// 将 num 赋给 index 处的元素 } /*4------ 删除索引 index 处的元素 */ void remove(int *nums, int size, int index) { for (int i = index; i < size - 1; i++) {// 把索引 index 之后的所有元素向前移动一位 nums[i] = nums[i + 1]; } } /*5------ 遍历数组 */ void traverse(int *nums, int size) { int count = 0; for (int i = 0; i < size; i++) {// 通过索引遍历数组 count += nums[i]; } } /*6------ 在数组中查找指定元素 */ int find(int *nums, int size, int target) { for (int i = 0; i < size; i++) { if (nums[i] == target) return i; } return -1; } /*7------ 扩展数组长度 */ int *extend(int *nums, int size, int enlarge) { int *res = new int[size + enlarge]; // 初始化一个扩展长度后的数组 for (int i = 0; i < size; i++) {// 将原数组中的所有元素复制到新数组 res[i] = nums[i]; } delete[] nums;// 释放内存 return res;// 返回扩展后的新数组 }
A—Advantages:
连续空间存储是一把双刃剑。
D—Disadvantages:
随机访问:随机抽取一些样本,可以用数组存储,并生成一个随机序列,根据索引实现随机抽样。
排序和搜索:快速排序、归并排序、二分查找等都主要在数组上进行。
查找表:当需要快速查找一个元素或其对应关系时,可以使用数组作为查找表。
假如我们想实现字符到 ASCII 码的映射,则可以将字符的 ASCII 码值作为索引,对应的元素存放在数组中的对应位置。
机器学习:神经网络中大量使用了向量、矩阵、张量之间的线性代数运算,这些数据都是以数组的形式构建的。
数组是神经网络编程中最常使用的数据结构。
数据结构实现:数组可以用于实现栈、队列、哈希表、堆、图等数据结构。例如,图的邻接矩阵表示实际上是一个二维数组。
链表(linked list)
是一种线性数据结构,其中的每个元素都是一个节点对象,各个节点通过“引用”相连接。
链表的组成单位是节点(node)
对象。每个节点都包含两项数据:节点的“值”和指向下一节点的“引用”。节点有 头节点 和 尾节点 。
尾节点指向的是“空”,在 Java、C++ 和 Python 中分别被记为 null、nullptr 和 None 。
链表节点 ListNode 除了包含值,还需额外保存一个引用(指针)。因此在相同数据量下,链表比数组占用更多的内存空间。
1:初始化链表
2:插入节点
3:删除节点
4:访问节点
5:查找节点
代码示例:
/*1------ 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ ListNode* n0 = new ListNode(1);// 初始化各个节点 ListNode* n1 = new ListNode(3); ListNode* n2 = new ListNode(2); ListNode* n3 = new ListNode(5); ListNode* n4 = new ListNode(4); n0->next = n1;// 构建节点之间的引用 n1->next = n2; n2->next = n3; n3->next = n4; /*2------ 在链表的节点 n0 之后插入节点 P ------只需改变两个节点引用(指针)即可*/ void insert(ListNode *n0, ListNode *P) { ListNode *n1 = n0->next; P->next = n1; n0->next = P; } /*3------ 删除链表的节点 n0 之后的首个节点 ------只需改变一个节点的引用(指针)即可*/ void remove(ListNode *n0) { if (n0->next == nullptr) return; ListNode *P = n0->next; // n0 -> P -> n1 ListNode *n1 = P->next; n0->next = n1; delete P;// 释放内存 } /*4------ 访问链表中索引为 index 的节点 ------在链表中访问节点的效率较低*/ ListNode *access(ListNode *head, int index) { for (int i = 0; i < index; i++) { if (head == nullptr) return nullptr; head = head->next; } return head; } /*5------ 在链表中查找值为 target 的首个节点 ------属于线性查找*/ int find(ListNode *head, int target) { int index = 0; while (head != nullptr) { if (head->val == target) return index; head = head->next; index++; } return -1; }
常见的链表类型包括三种:
单向链表:即普通链表。
环形链表:令单向链表的尾节点指向头节点(首尾相接),得到一个环形链表。其任意节点都可以视作头节点。
双向链表:与单向链表相比,双向链表记录了两个方向的引用。双向链表更灵活,可以朝两个方向遍历链表,但也占用更多的内存空间。
单向链表通常用于实现 栈、队列、哈希表和图等数据结构
。
双向链表常用于需要快速查找
前一个和后一个元素的场景。
环形链表常用于需要周期性操作
的场景,比如操作系统的资源调度。
————————————————————————————————————————————————————————————
—————————————————————Hello算法—速通笔记—第二集—end—————————————————————–—-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。