java-算法-集合-数据结构

算法

常见算法

二分法

ps:之前写的笔记没保存上,都没了,只能截了些图片

分块查找

核心思想:

块内无序,块间有序

实现步骤: .

1.创建数组blockArr存放每一个块对象的信息

2.先查找blockArr确定要查找的数据属于哪一块

3.再单独遍历这一 块数据即可

冒泡排序

核心思想:

1,相邻的元素两两比较,大的放右边,小的放左边。

2,第一轮比较完毕之后,最大值就已经确定,第二二轮可以少循环-一次,后面以此类推。

3,如果数组中有n个数据,总共我们只要执行n-1轮的代码就可以。

代码实现

        //1.定义数组
        int[] arr = {2, 4, 5, 3, 1};
        //2.利用冒泡排序将数组中的数据变成12345
        //外循环:表示我要执行多少轮。如果有n个数据, 那么执行n - 1轮
        for (int i = 0; i < arr.length - 1; i++) {
            //内循环:每- -轮中我如何比较数据并找到当前的最大值
            //-1:为了防止索引越界
            //-i:提高效率,每一轮执行的次数应该比上一轮少一次。
            for (int j = 0; j < arr.length - 1 - i; j++) {
                //i依次表示数组中的每一一个索引:日123 4
                if (arr[j] > arr[i + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }

选择排序

选择排序:

1,从0索引开始,跟后面的元素一一比较。

2,小的放前面,大的放后面。

3,第一次循环结束后,最小的数据已经确定。

4,第二次循环从1索引开始以此类推。

int[] arr = {2, 4, 5, 3, 1};

        //外循环:几轮
        //i:表示这一轮中,我拿着哪个索引上的数据跟后面的数据进行比较并交换
        for (int i = 0; i < arr.length - 1; i++) {
            //内循环:每一轮我要干什么事情?
            //拿着i跟i后面的数据进行比较交换
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[i] > arr[j]) {
                    int temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
            }
        }

插入排序

插入排序:将0索引的元素到N索引的元素看做是有序的,把N+1索引的元素到最后一个当成是无序的。遍历无序的数据,将遍历到的元素插入有序序列中适当的位置,如遇到相同数据,插在后面。

N的范围: 0~最大索引

快速排序

将排序范围中的第一个数字作为基准数,再定义两个变量start, end

start从前往后找比基准数大的,end从后往前找比基准数小的。

找到之后交换start和end指向的元素,并循环这一过程,直到start和end

处于同一个位置,该位置是基准数在数组中应存入的位置,再让基准数归位。

归位后的效果:基准数左边的,比基准数小,基准数右边的,比基准数大

代码实现:

int[]arr={6,1,2,7,9,3,4,5,10,8};
        /*参数一:我们要排序的数组
        参数二:要排序数组的起始索引
        参数三:要排序数组的结束索引*/
        quickSort(arr,0,arr.length-1);
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
    public static void quickSort(int[] arr, int i, int j) {

        //定义两个基准数
        int start = i;
        int end = j;

        if (start>end){
            return;
        }
        //记录基准数
        int baseNumber = arr[i];

        //利用循环找到要交换的数字
        while (start != end) {
            //利用end,从后往前开始找,找比基准数小的数字
            while (true) {
                if (end <= start || arr[end] < baseNumber) {
                    break;
                }
                end--;
            }

            while (true) {
                if (end <= start || arr[start] > baseNumber) {
                    break;
                }
                start++;
            }
            
            //把end和start指向的元素进行交换
            int temp = arr[start];
            arr[start] = arr[end];
            arr[end] = temp;
        }

        //基准数归位
        //就是拿着这个范围中的第一个数字, 跟start指向的元素进行交换
        int temp = arr[i];
        arr[i] = arr[start] ;
        arr[start] = temp ;

        //确定6左边的范围,重复刚刚所做的事情
        quickSort(arr,i,  start - 1);
        //确定6右边的范围,重复刚刚所做的事情
        quickSort(arr, start + 1,j);
    }

Arrays

操控数组的工具类

compar方法的形参

参数一o1: 表示在无序序列中,遍历得到的每一一个元素

参数二02:有序序列中的元素

返回值:

负数:表示当前要插入的元素是小的,放在前面

正数:表示当前要插入的元素是大的,放在后面

0:表示当前要插入的元素跟现在的元素比是一样的们也会放在后面

总结:o1-o2升序 反之降序

Lambda

函数式编程( Functional programming)

是一种思想特点。

函数式编程思想,忽略面向对象的复杂语法,强调做什么,而不是谁去做。

Lambda表达式就是函数式思想的体现。

基本作用

简化函数式接口的匿名内部类的写法

使用前提

必须是接口的匿名内部类,接口中只能有一个抽象方法

好处 更简洁,更灵活

Integer[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
        Arrays.sort(arr, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        //lambda完整格式

        Arrays.sort(arr, (Integer o1,Integer o2) -> {
            return o1 - o2;
        }
        );
        //lambda省略写法
        Arrays.sort(arr, (o1, o2) -> o1 - o2);
        System. out . println(Arrays. toString(arr));

练习

String[] arr = {"a", "aaa", "aaa", "aa"};
        //如果以后我们要把数组中的数据按照指定的方式进行排列,就需要用到sort方法,而且要指定排序的规则
        Arrays. sort(arr, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                //字符串的长度进行排序
                return o1.length() - o2.length();
            }
            });
        //Lambda完整格式
        Arrays. sort(arr, (String o1, String o2)->{
            return o1.length() - o2.length();
        }
        );
        //Lambda简写格式
        //小括号:数据类型可以省略,如果参数只有一个,小括号还可以省略
        //大括号:如果方法体只有一行,return, 分号,大括号都可以省略
        Arrays . sort(arr, (o1, o2)-> o1.length() - o2.length());

集合进阶

集合体系结构

Collection集合

Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。

/*注意点:
        Collection是一个接口,不能直接创建他的对象。
        所以学习他的方法时,只能创建他实现类的对象。
        实现类:
        ArrayList*/
        //目的:为了学习Collection接口里面的方法
        //在做一些练习的时候,还是按照之前的方式去创建对象。
        Collection<String> co1l = new ArrayList<>();


        //判断集合中某-一个学生对象是否包含
        Student s4 = new Student(" zhangsan", 23);
        co1l.add(s4)
        //因为contains方法在底层依赖equals方法判断对象是否一致的。
        //如果存的是自定义对象,没有重写equals方法,那么默认使用object 类中的equals方法进行判断,而0bject 类中equals方法,依赖地址值进行判断。
        //需求:如果同姓名和同年龄,就认为是同-一个学生。
        //所以,需要在自定义的Javabean类中,要重写equals方法。
        System.out.println(coll.contains(s4));

Collection的遍历方式

迭代器遍历

细节注意点:

1,如果该位置没有元素,还获取会报错NoSuchElementException

2,迭代器遍历完毕,指针不会复位

3,,循环中只能用一次next方法

4,迭代器遍历时,不能用集合的方法进行增加或者删除

如果真的要删除,可以用迭代器的remove方法,或者等遍历结束

代码实现

//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll. add("aaa");
coll. add("bbb");
coll.add("ccc");
coll. add("ddd");
//2.获取迭代器对象
//迭代器就好比是-一个箭头,默认指向集合的0索引处
Iterator<String> it = coll. iterator();
//3.利用循环不断的去获取集合中的每-一个元素
while(it . hasNext()){//判断返回值是否为空
//4. next方法的两件事情:获取元素并移动指针
String str = it.next();
System. out . println(str);
}

增强for遍历

所有的单列集合和数组才能用增强for进行遍历。

格式:

for(元素的数据类型 变量名:数组或者集合){

}

//1.创建集合并添加元素
        Collection<String> coll = new ArrayList<>();
        coll.add("aaa");
        coll.add("bbb");
        coll.add("ccc");
        coll.add("ddd");
        //2.利用增强for进行遍历
        //注意点:
        //s其实就是一个第三方变量,在循环的过程中依次表示集合中的每一个数据
        for (String s : coll) {
            System.out.println(s);
        }
        //快速生成方式
        //集合的名字+for回车

细节:修改增强for中的变量,不会改变集合中原本的数据

Lambda表达式遍历

//1.创建集合并添加元素
        Collection<String> coll = new ArrayList<>();
        coll.add("aaa");
        coll.add("bbb");
        coll.add("ccc");
        coll.add("ddd");

        //2.用匿名内部类
        coll.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });

        //lambda表达式
        coll.forEach(s -> System.out.println(s));

List集合

特点

有序:存和取的元素顺序一致

有索引:可以通过索引操作元素

可重复:存储的元素可以重复

特有方法

删除方法

//List系列集合中的两个删除的方法
//1.直接删除元素
//2.通过索引进行删除
//1.创建集合并添加元素
List<Integer> list = new ArrayList<>();
list.add(1);
list . add(2);
list.add(3);
//2.删除元素.
//请问:此时删除的是1这个元素,还是1索引上的元素?
//为什么?
//因为在调用方法的时候,如果方法出现了重载现象
//优先调用,实参跟形参类型一致的那个 方法。
//list. remove(1); 
//手动装箱,手动把基本数据类型的1,变成Integer类 型
Integer i = Integer . value0f(1);
list. remove(i);
System. out . println(list);

遍历方式:

1.迭代器

2.列表迭代器

List<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        // 5.列表迭代器
        //获取一个列表迭代器的对象,里面的指针默认也是指向0索引的
        //额外添加了一个方法:在遍历的过程中,可以添加元素
        ListIterator<String> it = list.listIterator();
        while (it.hasNext()) {
            String str = it.next();
            if ("bbb".equals(str)) {
                //用遍历器的方法添加元素
                it.add("qqq");

            }
        }

3.增强for

4. Lambda表达式

5.普通for循环

总结

ArrayList集合

底层逻辑

LinkedList集合

底层为双向指针 便于增删

泛型

泛型的细节

●泛型中不能写基本数据类型

●指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型

●如果不写泛型,类型默认是Object

泛型类

使用场景:当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类 <E>

泛型方法:

方法中形参类型不确定时,可以使用类名后面定义的泛型<E>

泛型的通配符:

?也表示不确定的类型 相当于E

他可以进行类型的限定

? extends E:表示可以传递E或者E所有的子类类型

? super E:表示可以传递E或者E所有的父类类型

应用场景:

1.如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口。

2.如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以泛型的通配符

关键点:可以限定类型的范围。

泛型不具备继承性,但数据具备继承性

Set集合

●无序:存取顺序不一致

●不重复:可以去除重复

●无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素

实现类特点

●HashSet: 无序、不重复、无索引

●LinkedHashSet: 有序、不重复、无索引

●TreeSet: 可排序、不重复、无索引

HashSet

底层原理

注意:

JDK8以后, 当链表长度超过8,而且数组长度大于等于64时,自动转换为红黑树

如果集合中存储的是自定义对象,必须要重写hashCodeequals方法

LinkedHashSet

这里的有序指的是保证存储和取出的元素顺序一致

原理:底层数据结构是依然哈希表,只是每个元素又额外的多了-个双链表的机制记录存储的顺序。

数据去重:默认使用HashSet

如果要求去重且存取有序,才使用LinkedHashSet

TreeSet

可排序:按照元素的默认规则(有小到大)排序。

TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。

排序方式

默认排序/自然排序: Javabean类 实现Comparable接口指定比较规则

先实现接口

再重写方法

比较器排序:创建TreeSet对象时候,传递比较器Comparator指定规则

        //1.创建集合
        //o1:表示当前要添加的元素
        //o2:表示已经在红黑树存在的元素
        //返回值规则跟之前是一样的
        TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                // 按照长度排序
                int i = o1.length() - o2.length();
                //如果一样长则按照首字母排序
                i = i == 0 ? o1.compareTo(o2) : i;
                return i;
            }
            });
        //2.添加元素
        ts.add("c");
        ts.add("ab");
        ts.add("df");
        ts.add("qwer");
        //3.打印集合
        System.out.println(ts);

注:如果这两种都重写了,按照第二种

总结

双列集合

特点

①双列集合一次需要存一对数据,分别为键和值

②键不能重复,值可以重复

③键和值是一 一对应的,每一个键只能找到自己对应的值

④键+值这个整体我们称之为“键值对”或者“键值对对象”,在Java中叫做"Entry对象”

Map

常见API

代码实现

        //1.创建Map集合的对象,new实现类对象
        Map<String, String> m = new HashMap<>();
        //2.添加元素
        //put方法的细节:
        //添加/覆盖
        //在添加数据的时候,如果键不存在,那么直接把键值对对象添加到map集合当中
        //在添加数据的时候,如果键是存在的,那么会把原有的键值对象覆盖,会把被覆盖的值进行返回。
        m.put("郭靖", "黄蓉");
        m.put("韦小宝", "沐剑屏");
        m.put("尹志平", "小龙女");

        String valua = m.put("韦小宝", "双儿");//沐剑屏
        System.out.println(valua);
        //3.打印集合
        System.out.println(m);
Map的遍历方式

键找值

        //1.创建Map集合的对象,new实现类对象
        Map<String, String> m = new HashMap<>();
        //2.添加元素
        m.put("郭靖", "黄蓉");
        m.put("韦小宝", "沐剑屏");
        m.put("尹志平", "小龙女");

        //3.通过键找值
        //3.1获取所有的键,把这些键放到一个单列集合当中
        Set<String> keys = m.keySet();
        //3.2遍历单列集合,得到每一一个键
        for (String key : keys) {
            //System. out . println(key);
            //3.3 利用map集合中的键获取对应的值 get
            String value = m.get(key);
            System.out.println(key + "=" + value);
        }
        //迭代器遍历
        Iterator<String> i = keys.iterator();
        while (i.hasNext()){
            String s = i.next();
            String value = m.get(s);
            System.out.println(s + "=" + value);
        }

键值对

//3.Map集合的第二种遍历方式
        //通过键值对对象进行遍历
        //3.1 通过一个方法获取所有的键值对对象,返回一个Set集合
        Set<Map.Entry<String, String>> entries = map.entrySet();
        //3.2遍历entries这个集合,去得到里面的每一一个键值对对象
        for (Map.Entry<String, String> entry : entries) {
        //3.3利用entry调用get方法获取键和值
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "=" + value);
        }

Lambda表达式

        //3.利用lambda表达式进行遍历
        map. forEach(new BiConsumer<String, String>() {
            @Override
            public void accept(String key, String value) {
                System. out . println(key + "=" + value);
            }
        });
        
        map.forEach((key,value)-> System.out.println(key+"="+value));
HashMap

特点:

①HashMap是Map里面的一个实现类。

②没有额外需要学习的特有方法,直接使用Map里面的方法

③特点都是由键决定的:无序、不重复、无索引

④HashMap跟HashSet底层原理是一模一样的,都是哈希表结构

总结

1.依赖hashCode方法和equals方法保证键的唯一

2.如果键存储的是自定义对象,需要重写hashCode和equals方法

如果值存储自定义对象,不需要重写hashCode和equals方法

LinkHashMap

由键决定:有序、不重复、无索引。

●这里的有序指的是保证存储和取出的元素顺序一致

原理:底层数据结构是依然哈希表,只是每个键值对元素又额外的多了- -个双链表的机制记录存储的顺序。

TreeMap

●TreeMap跟TreeSet底层原理一 样,都是红黑树结构的。

●由键决定特性:不重复、无索引、可排序

●可排序:对键进行排序。

●注意:默认按照键的从小到大进行排序,也可以自己规定键的排序规则

可变参数

本质上就是一个数组

作用:在形参中接收多个数据

格式:数据类型..参数名称

举例: int...a

注意事项:

●形参列表中可变参数只能有一个

●可变参数必须放在形 参列表的最后面

Collections

●java.util.Collections:是集合工具类

●作用: Collections不是集合,而是集合的工具类。

常用API

数据结构

栈:

后进先出,先进后出

队列:

先进先出,后进后出

数组:

查询快,增删慢

链表:

增删相对较快

二叉树的内部结构

父节点地址,值,左子节点,右子节点

二叉查找树

每一个节点上最多有两个子节点

任意节点左子树上的值都小于当前节点

任意节点右子树上的值都大于当前节点

前序遍历:从根结点开始,然后按照当前结点,左子结点,右子结点的顺序遍历

中序遍历:从最左边的子节点开始,然后按照左子结点,当前结点,右子结点的顺序遍历

后序遍历:从最左边的子节点开始,然后按照左子结点,右子结点,当前结点的顺序遍历

层序遍历:一层一层的遍历

平衡二叉树

任意节点子树高度不超过二

需要旋转的情况

黑树

1.每一个节点或是红色的,或者是黑色的

2.根节点必须是黑色

3.如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的

4.如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)

5.对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点;

拓展

不可变集合

在List、Set、 Map接口中, 都存在静态的of方法,可以获取一个不可变的集合。

细节:

●List: 直接用

●Set: 元素不能重复

●Map:元素不能重复、键值对数量最多是10个。

超过10个用ofEntries方法

//1.创建一个 普通的Map集合
        HashMap<String, String> hm = new HashMap<>();
        hm.put("张三", "南京");
        hm.put("李四", "北京");
        hm.put("王五", "上海");

        //2.利用上面的数据来获取-一个不可变的集合
        //获取到所有的键值对对象(Entry对象)
        Set<Map.Entry<String, String>> entries = hm.entrySet();
        //把entries变成一个数组
        Map.Entry[] arr1 = new Map.Entry[0];
        //toArray方法在底层会比较集合的长度跟数组的长度两者的大小
        //如果集合的长度>数组的长度:数据在数组中放不下,此时会根据实际数据的个数,重新创建数组
        //如果集合的长度<=数组的长度:数据在数组中放的下,此时不会创建新的数组,而是直接用
        Map.Entry[] arr2 = entries.toArray(arr1);//把键值变成指定的数组
        //不可变的map集合
        Map map = Map.ofEntries(arr2);

Map的不可变集合

  1. 键是不能重复的

  1. Map里面的of方法,参数是有上限的,最多只能传递20个参数,10 个键值对

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值