1:什么叫数据结构 什么叫算法

2021-05-13  本文已影响0人  _River_
学习连接来源于算法小抄:https://labuladong.gitee.io/algo/
1:什么叫数据结构
数据结构的存储方式只有两种:数组(顺序存储)和链表(链式存储)。

ArrayList   LinkedList
Array   英文为  数组 
Linked 英文为  链接
List     英文为  集合 清单

因此建议以后描述为:
基本数组:最基本的数组 int[ ] array ;
数组集合(数组):ArrayList
链表集合(链表):LinkedList

特别是针对数组建议区分描述;

那么栈、队列、散列表(Hash表)、堆、树、图  这些数据结构呢?

实际上那些都属于「上层建筑」,而数组和链表才  是「结构基础」。因为那些多样化的数据结构,究其源头,都是在链表或者数组上的特殊操作,API 不同而已。

比如说「队列」、「栈」这两种数据结构既可以使用链表也可以使用数组实现。
用数组实现,就要处理扩容缩容的问题;用链表实现,没有这个问题,但需要更多的内存空间存储节点指针。
    
「散列表」(Hash表)就是通过散列函数把键映射到一个大数组里,
而且对于解决散列冲突的方法:
方法一:拉链法要链表特性,操作简单,但需要额外的空间存储指针;
方法二:线性探查法就需要数组特性,以便连续寻址,不需要指针的存储空间,但操作稍微复杂些;
      
 [堆]
用数组实现就是「堆」,因为「堆」是一个完全二叉树,用数组存储不需要节点指针,操作也比较简单;

「树」   
用链表实现就是很常见的那种「树」,因为不一定是完全二叉树,所以不适合用数组存储。
为此,在这种链表「树」结构之上,又衍生出各种巧妙的设计,比如二叉搜索树、AVL 树、红黑树、区间树、B 树等等,以应对不同的问题。
     
「图」的两种表示方法,邻接矩阵就是二维数组。邻接表就是链表。
邻接矩阵判断连通性迅速,并可以进行矩阵运算解决一些问题,但是如果图比较稀疏的话很耗费空间。
邻接表比较节省空间,但是很多操作的效率上肯定比不过邻接矩阵。


数组由于是紧凑连续存储,可以随机访问,通过索引快速找到对应元素,而且相对节约存储空间。
但正因为连续存储,内存空间必须一次性分配够,所以说数组如果要扩容,需要重新分配一块更大的空间,再把数据全部复制过去,时间复杂度 O(N);
而且你如果想在数组中间进行插入和删除,每次必须搬移后面的所有数据以保持连续,时间复杂度 O(N)。


链表因为元素不连续,而是靠指针指向下一个元素的位置,所以不存在数组的扩容问题;
如果知道某一元素的前驱和后驱,操作指针即可删除该元素或者插入新元素,时间复杂度 O(1)。
但是正因为存储空间不连续,你无法根据一个索引算出对应元素的地址,所以不能随机访问;而且由于每个元素必须存储指向前后元素位置的指针,会消耗相对更多的储存空间。
2:什么叫算法
所谓的算法就是基于对应的数据结构做对应操作

对于任何数据结构,其基本操作无非遍历 + 访问,再具体一点就是:增删查改。
数据结构种类很多,但它们存在的目的都是在不同的应用场景,尽可能高效地增删查改。

数组遍历框架,典型的线性迭代结构:
void traverse(int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        // 迭代访问 arr[i]
    }
}
链表遍历框架,兼具迭代和递归结构:  
/* 基本的单链表节点 */
class ListNode {
    int val;
    ListNode next;
}

void traverse(ListNode head) {
    for (ListNode p = head; p != null; p = p.next) {
        // 迭代访问 p.val
    }
}

void traverse(ListNode head) {
    // 递归访问 head.val
    traverse(head.next);
}

二叉树遍历框架,典型的非线性递归遍历结构:
/* 基本的二叉树节点 */
class TreeNode {
    int val;
    TreeNode left, right;
}

void traverse(TreeNode root) {
    traverse(root.left);
    traverse(root.right);
}

二叉树框架可以扩展为 N 叉树的遍历框架:
/* 基本的 N 叉树节点 */
class TreeNode {
    int val;
    TreeNode[] children;
}

void traverse(TreeNode root) {
    for (TreeNode child : root.children)
        traverse(child);
}

3:如何快速的提高对数据结构以及算法的理解
先刷二叉树呢,因为二叉树是最容易培养框架思维的,而且大部分算法技巧,本质上都是树的遍历问题。
不要小看这几行破代码,几乎所有二叉树的题目都是一套这个框架就出来了。
void traverse(TreeNode root) {
    // 前序遍历
    traverse(root.left)
    // 中序遍历
    traverse(root.right)
    // 后序遍历
}

不要心急  先保留问题 等学完再回到这里
不要心急  先保留问题 等学完再回到这里
不要心急  先保留问题 等学完再回到这里
int ans = INT_MIN;
int oneSideMax(TreeNode* root) {
    if (root == nullptr) return 0;
    int left = max(0, oneSideMax(root->left));
    int right = max(0, oneSideMax(root->right));
    ans = max(ans, left + right + root->val);
    return max(left, right) + root->val;
}

TreeNode buildTree(int[] preorder, int preStart, int preEnd, 
    int[] inorder, int inStart, int inEnd, Map<Integer, Integer> inMap) {

    if(preStart > preEnd || inStart > inEnd) return null;

    TreeNode root = new TreeNode(preorder[preStart]);
    int inRoot = inMap.get(root.val);
    int numsLeft = inRoot - inStart;

    root.left = buildTree(preorder, preStart + 1, preStart + numsLeft, 
                          inorder, inStart, inRoot - 1, inMap);
    root.right = buildTree(preorder, preStart + numsLeft + 1, preEnd, 
                          inorder, inRoot + 1, inEnd, inMap);
    return root;
}

void traverse(TreeNode* node) {
    if (!node) return;
    traverse(node->left);
    if (node->val < prev->val) {
        s = (s == NULL) ? prev : s;
        t = node;
    }
    prev = node;
    traverse(node->right);
}

上一篇 下一篇

猜你喜欢

热点阅读