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

本文介绍了数据结构与算法的关系,重点分析了数组和链表的特性。数组支持随机访问,时间复杂度为O(1),但插入和删除操作可能需要O(n)的时间。链表插入和删除操作为O(1),但查找时间复杂度较高。文章还讨论了复杂度分析的基本法则,并提到全局变量存储于方法区。
摘要由CSDN通过智能技术生成

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函数中 稳如老狗。

全局变量是存储在哪里?

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值