学习笔记——数据结构与算法之美_极客时间

1、要掌握的:

10个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树;

10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法

算法和数据结构的关系:数据结构是为算法服务的,算法要作用在特定的数据结构之上

 2、基本功:复杂度分析

我们需要一个不用具体的测试数据来测试,就可以粗略地估计算法的执行效率的方法,了解你的代码运行质量!

所有代码的执行时间T(n) 与每行代码的执行次数成正比. 即: 每行代码的执行次数越多,所有代码的执行时间就越长. 每行代码的执行次数越少,所有代码的执行时间就越短.

从最初每行代码的时间都要计算在内,叫T(n), 大 O 时间复杂度实际上并不具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的变化趋势,其忽略了低阶、常量、系数。

最终,我们分析时间复杂度 都是 说 量级 O(n)、O(n方) 

如何时间复杂度分析总结:

1. 只关注循环执行次数最多的一段代码

2. 加法法则:总复杂度等于量级最大的那段代码的复杂度

3. 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积

常见的复杂度量级如下:

数组:

是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。

特点:数组支持随机访问,根据下标随机访问的时间复杂度为 O(1)。

想要查找数组里的某个值,算法复杂度取决于用的什么算法,查找是查找指定的值所在的下标(存储地址),需要不断拿内存中存储的value来比较,而访问是给定下标也就是value的存储地址去获取value,直接寻址就行了。看似很相近的两个词,表述出来所表达的意思刚还是相反的。

插入:

最好情况:插入最后一位 O(1),最坏插入第一位后面的均后移 O(n),平均:(1+2+3..n)/n=O(n)

优化:某些场景下,直接调换指定的位置数据就可以了,不需要把后面的数据整体移动。如插入第3位,则 先插入最后一位,再和第三位进行调换。

删除:

最好情况:删除尾部,O(1),最坏情况,删除第一个,后面的均向前O(n),平均为 O(n)

优化:在某些特殊场景下,我们并不一定非得追求数组中数据的连续性。如果我们将多次删除操作集中在一起执行,删除的效率是会提高很多。如:先记录下已经删除的数据,每次每次的删除操作并不是真正地搬移数据,只是记录数据已经被删除。当数组没有更多空间存储数据时,我们再触发执行一次真正的删除操作,这样就大大减少了删除操作导致的数据搬移。(应用在jvm的垃圾标记删除上)

查找:

因为是寻找访问,所以支持随机查询。

c语言悬疑案例:(java有数组越界检查,因为java创建数组要先指定大小)


int main(int argc, char* argv[]){
    int i = 0;
    int arr[3] = {0};
    for(; i<=3; i++){
        arr[i] = 0;
        printf("hello world\n");
    }
    return 0;
}


//将无限输出  hello world

数值为什么下标从0开始?

因为  a[k]_address = base_address + k * type_size,如果从1开始,每次随机访问时多一次计算,对cpu来说是多了一次指令,不过最可能的是历史原因,沿用。

链表:

插入:O(1),相当于在数值最后面插入 

删除:O(1)

 查找:需要一个一个往下找

 

 占空间,删除和插入比单链表高效

总结:数组相比,链表更适合插入、删除操作频繁的场景,查询的时间复杂度较高

 

//链表反转

public class temp {
    public static void main(String[] args) {
        //创建一个点链表
        SingNode head = new SingNode(0);
        SingNode headTemp = head; //记录头节点, 记录后head变化后,headTemp的值并不受影响
        for (int i = 1; i < 10; i++) {
            SingNode nextNode = new SingNode(i);
            head.next = nextNode;
            head = nextNode;
        }
        //输出链表
        soutNode(headTemp);

        //链表反转的实现

       SingNode headLastTemp = getLastSingNode(headTemp);
        SingNode headLastTempTemp=headLastTemp;

        while (headTemp != null) {
            //每次获取最后一个节点
            SingNode lastSingNode = getLastSingNode(headTemp);
            //发现获取最后的一个节点 和头节点相等 说明 已经只有一个节点了
            if(lastSingNode==headTemp){
                break;
            }
            headLastTemp.next=lastSingNode;
            headLastTemp=lastSingNode;
        }
        soutNode(headLastTempTemp);

    }

    private static SingNode getLastSingNode(SingNode headTemp) { //这个值 是传过来的是 地址, 在函数中 你可以对地址 对应的 对象 进行操作,但是对地址是修改不了的
        SingNode result = null;
        while (headTemp!=null) {
            //说明就剩1个元素
          if(headTemp.next==null){
              result=headTemp;
              break;
          }
            if (headTemp.next.next == null) {
                result = headTemp.next;
                //删除最后一个节点
                headTemp.next = null;
                break;
            }
            headTemp=headTemp.next;
        }
        System.out.println(result.data);
        return result;
    }

    private static void soutNode(SingNode headTemp) {
        while (headTemp != null) {
            System.out.print(headTemp.data + " ");
            headTemp = headTemp.next;
        }
    }
}


class SingNode {
    Integer data;
    SingNode next;

    public SingNode(int data) {
        this.data = data;
    }

}

 上述代码遇到的坑:想在函数中,让链表的头重置,其实是无效的,这和java的特性有关,



void A(){
 B(student)
}


//是控制不了 A函数中的 student对象地址的, 但能控制 student 对象的属性
void B(Sturdent student){
 student=null; 
}

Java中其实还是值传递的,只不过对于对象参数,值的内容是对象的引用。 

因此,基本类型是值拷贝,对象类型的是 地址值拷贝

因此,我们有了 地址值 是能找到 那个对象的,因此 我们可以对 该对象的属性 进行改动,但是休想 对 地址值改动。 向上面的代码,B想修改student的地址值,在B内是有效的值null,但是在A函数中 稳如老狗。

全局变量是存储在哪里?

存在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。(全局变量是可以看作是 某个类的 字段属性)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
逻辑结构:描述数据元素之间的逻辑关系,如线性结构(如数组、链表)、树形结构(如二叉树、堆、B树)、图结构(有向图、无向图等)以及集合和队列等抽象数据类型。 存储结构(物理结构):描述数据在计算机中如何具体存储。例如,数组的连续存储,链表的动态分配节点,树和图的邻接矩阵或邻接表表示等。 基本操作:针对每种数据结构,定义了一系列基本的操作,包括但不限于插入、删除、查找、更新、遍历等,并分析这些操作的时间复杂度和空间复杂度。 算法算法设计:研究如何将解决问题的步骤形式化为一系列指令,使得计算机可以执行以求解问题。 算法特性:包括输入、输出、有穷性、确定性和可行性。即一个有效的算法必须能在有限步骤内结束,并且对于给定的输入产生唯一的确定输出。 算法分类:排序算法(如冒泡排序、快速排序、归并排序),查找算法(如顺序查找、二分查找、哈希查找),图论算法(如Dijkstra最短路径算法、Floyd-Warshall算法、Prim最小生成树算法),动态规划,贪心算法,回溯法,分支限界法等。 算法分析:通过数学方法分析算法的时间复杂度(运行时间随数据规模增长的速度)和空间复杂度(所需内存大小)来评估其效率。 学习算法数据结构不仅有助于理解程序的内部工作原理,更能帮助开发人员编写出高效、稳定和易于维护的软件系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值