集合
数据结构基本知识
基本数据结构
数据结构:
数据:计算机中存储元素
结构:数据与数据之间的关系
常用的数据和数据之间的关系:
数据结构包括两个层次(角度)
(1)逻辑结构:数据与数据之间的逻辑结构
包含四种结构:线性结构,树形结构,图状结构,集合结构
(2)物理结构:数据在计算机中真实的存储结构
包含两种:顺序存储和链式存储(在内存片上,一个是连续存储,一个是不连续存储)
常用的几种结构:
(1)数组(一位线性存储)物理角度 一维顺序线性表
(2)链表
(3)堆栈
(4)队列
(5)树
(6)哈希表
其中数组、链表、堆栈、队列-------逻辑上是线性存储
堆栈、队列:受限的线性存储
特殊说明:
会把常用的数据结构:分为线性表(顺序线性表、线性链表)。。。数组概念升级成特殊的
角度不同:
数组:(从物理结构角度)
线性表:(从逻辑结构角度)
顺序线性表:数组(一维数组)+线性表
1.数组
从物理结构上划分的数组,不是java中的数组
【定义】使用一组地址连续的存储单元,依次存储线性表的数组元素
【数组中元素的访问】
每个元素所占用的空间是一样的size大小
首地址+偏移量(n*size)
对于每个元素访问时候的效率都是一样的,O(1)常数时间复杂度
【使用场景】
(1)java中的数组使用数据结构中结构中的静态数组,一组地址连续的存储单元
思考 为什么java中的数组但初始化了,就不能修改长度
先申请一片内存,要存储十个,最后一个元素length属性
原因:如果扩充了java中的数组大小,无法断定连续地址的后面有没有内存空间,如果没有空间,接着其他的
java的数变成非连续的存储,无法访问
(2)数据结构中“动态数组”
动态数组:可以进行库容,也是在原来的静态数组的基础上形成,代价:效率慢
List list=new ArrayList(); 就是使用动态数组 来存储
2.链表
一个元素“挨着”另一个元素,不是非要连续存储
【定义】是线性(数据和数据之间的关系),但是不是连续存储
链表中的节点可以分成两个部分:
主句与:存储数据
指针域:下一个元素的地址
随机访问性:没有数组好,访问每个元素的效率不同 时间复杂度O(0)
对于元素进行插入和删除效率比数组高:只需要修改指针域即可
大多数的存储都是优先选择数据结构
【了解】链表:单项链表和双向链表
双向链表:一定程度上优化了单项链表的访问性,但是增加了添加元素,删除元素的复杂度
3.堆栈
栈:线性存储(元素和元素之间有关系)受限制的线性表
栈顶:口()出入口
栈底:第一个进入的元素所放的位置
规则:先进后出LIFO
【使用场合】
方法的调用
A–B--C–D--E目的是为了接住返回值
异常的向上传播,异常抛出给调用者
4.队列
队列:线性存储(为了表达元素和元素之间的关系),受限的线性表
队头:第一个进入的元素:(出)
队尾:最后一个进入的元素:(入)
规则:先进先出FIFO
【场合】处理多线程
【了解】也可以有双端队列,可以进行队头和队尾的添加和删除元素,可以实现栈的存储
缺点:添加和删除元素
5.树
节点集合连接每对节点的有向边集组成
二叉树:特征每个节点(除叶子节点以外)都只有不超过两个节点(0个,1个,2个)
满二叉树:除叶子节点以外,每个节点都有两个子节点。
对满二叉树进行编号:从上到下,从左到右
完全二叉树:当一个二叉树的所有节点编号,都跟满二叉树保持一致,称为完全二叉树
遍历树:对树中所有的节点进行访问
广度遍历:按照层次遍历,先爷爷,找父辈或者父辈兄弟
深度遍历:爷爷–父亲–儿子
掌握的内容:对于二叉树的遍历:
针对于“根节点”
先序遍历:中左右
中序遍历:左中右
后序遍历:左右中
6.哈希表
希望将一组数据存储到内存中一组有序的存储单元中(不是按照一对一的存储)
存储规则:按照哈希规则进行存储
数学中的规则y=f(x)哈希函数的规则
假如:y=x%7 一定会将众多的x存储到7个存储单元中
【好处】当我们访问元素的时候。都可以是常数时间复杂度
【问题】都会采用hashcode中扩展,两种方法,重新计算hashcode,存入下一个地址
也可以对单一地址进行扩展,扩展成新的数组
将一组关键字(数据)根据哈希函数(规则),映射到一个有限的地址集上,这种结构的表称为hash表
【场合】
java中的map key value
查找和排序
查找:准确的说查找的方法有三种:顺序查找;二分查找;分块查找
排序:冒泡、选择、插入、希尔排序、快速、归并
插入排序
【规则】:从第二个元素开始,插入到现有的已排好序的数组中,保证前小后大的排序
希尔排序
【规则】:改进版的插入排序(最小增量排序)分组
增量:inc 将i个元素和i+inc、i+2inc 、i+3inc…
每次进行分组,对每一组的元素进行(插入)排序,注意的是,每个元素的原来位置保持不变
增量设置的规则:没有 原则:从大到小 每次取一半 6 3 1
假设增量为3 2 1
public class Day13_1_con {
// 希尔排序
public static void shell(int[] t,int []increment){
//int[] t={3,-2,5,10,-9,11,7,-1};
for (int inc : increment) {
// 每一个增量下的每一组
for(int k=0;k<inc;k++){
for(int i=k+inc;i<t.length;i+=inc){
int temp=t[i];
int j= i-inc;
while(j>=0&&temp<t[j]){
t[j+inc]=t[j];
j-=inc;
}
t[j+inc]=temp;
System.out.println(Arrays.toString(t));
}
}
}
}
// 插入排序
public static void insert(int []t){
//int[] t={3,-2,5,10,-9,11,7,-1};
//int[] t={-2,3,5,10,-9,11,7,-1};
//int[] t={-9,-2,3,5,10,11,7,-1};
//int[] t={-9,-2,3,5,j+1,10,11,-1}; temp=7
for(int i=1;i<t.length;i++){
// int i=1; //没有排好对的第一个元素(正在排序的元素)
int temp=t[i];//正在排序的元素值
int j= i-1; //排好队的最后一个元素
while(j>=0&&temp<t[j]){
t[j+1]=t[j];
j--;
}
t[j+1]=temp;
}
}
// 每次都找到最小的元素,放在前面(注意:每次找到最小值的索引,再跟当前元素交换位置)
public static void choose(int []t){
for(int i=0;i<t.length-1;i++){
int min_index=i;
for(int j=i+1;j<t.length;j++){
if (t[min_index]>t[j]){
min_index=j;
}
}
int temp=t[i];
t[i]=t[min_index];
t[min_index]=temp;
}
}
// 相邻的两个元素之间进行比较,如果不符合前小后大的的原则,交换
public static void bubble(int []t){
boolean tag=true;
for (int j=0;j<t.length-1;j++){
for(int i=0;i<t.length-1-j;i++){
if(t[i]>t[i+1]){
int temp=t[i];
t[i]=t[i+1];
t[i+1]=temp;
tag=false;
}
}
if(tag){
break;
}
}
}
public static void binarySearch(int[] t,int key){
Arrays.sort(t);
int start=0;
int end=t.length-1;
boolean tag=true;
while(start<=end){
int mid=(start+end)/2;
if(key==t[mid]){
System.out.println("找到了");
tag=false;
break;
}else if(key<t[mid]){
end=mid-1;
}else{
start=mid+1;
}
}
if(tag){
System.out.println("不存在");
}
}
public static void main(String[] args) {
int[] t={3,-2,5,10,-9,11,7,-1};
// binarySearch(t,30);
// bubble(t);
// insert(t);
// choose(t);
shell(t,new int[]{3,2,1});
System.out.println(Arrays.toString(t));
}
}
集合
【集合】容器类,集合也可以看成一个容器,能够容纳多个元素的容器
数组的最大缺点:声明之后,长度不能再更改,同时,对于数组操作也很麻烦
Collection接口
所有集合类的根接口,最直接的子接口Set List
Map是独立出来的接口:键值对的形式存储数据
(一)collection接口
不能实例化对象。选择ArrayList作为实现类,来创建对象
接口引用 c=new ArrayList();
c.add(); AarryList下实现的方法
1.集合下的相关方法
public class Day13_2_Collection {
public static void main(String[] args) {
Collection<Integer> c=new ArrayList<Integer>();
Collection<Integer> c2=new ArrayList<Integer>();
添加相关
(1)add 向集合中添加元素,如果添加成功会返回true,否则返回false
当实现类是Set的实现类的时候,如果有重复元素,则不能添加成功
System.out.println(c.add(11));
(2)addAll(集合类型的对象) 将参数指定的所有元素都加入到当前集合
返回值:当前集合发生变化的时候,就会返回true,否则则返回false(如果一个都加不进去才会false)
c2.add(22);
c.addAll(c2);
for (Integer integer : c) {
System.out.println(integer);
}
跟集合大小
(3)size 返回集合中元素的个数
判断一个集合类型是不是空(没有元素)的,尽量不要用对象!=null
public List a(){ return list.getSize()>0?list:null;}
其他调用a的人 List b=a(); if(b=null)
System.out.println(c==null);
(4)isEmpty判断集合是否为空
System.out.println(c.isEmpty());
(5)判断集合是否包含某一个对象
c.add(10);
System.out.println(c.contains(10));
(6)c.containsAll(另外一个集合) 判断c中是否包含另外一个集合中的所有元素
c2.add(10);
c.add(10);
c.add(11);
System.out.println(c.containsAll(c2));
跟删除相关
(7)remove(Object o)删除集合中的某个元素
当删除不成功(没有这个元素),会返回false,成功则返回true
c.add(10);
c.add(11);
System.out.println(c.remove(12));
for (Integer integer : c) {
System.out.println(integer);
}
(8)removeAll(另外一个集合) 删除集合中,另外一个集合的所有元素(删除交集)
如果当前集合发生变化,返回true,如果一个元素都没删除,则返回false
c2.add(10);
c.add(11);
c.addAll(c2);
c.removeAll(c2);
for (Integer integer : c) {
System.out.println(integer);
}
(9)c.removeIf(filter) Predicate中test方法:按照实现类的规则进行筛选数据。
for (int i = 0; i < 10; i++) {
c.add(i);
}
c.removeIf((Integer t)->t>5);
for (Integer integer : c) {
System.out.println(integer);
}
(10)retainall(集合类型):将参数集合中存在于当前集合的元素保留下来(保留下交集)
for (int i = 0; i < 10; i++) {
c.add(i);
}
for (int i = 0; i < 9; i++) {
c2.add(i);
}
c.retainAll(c2);
for (Integer integer : c) {
System.out.println(integer);
}
c.forEach(System.out::println);
(11)clear清空给所有元素
c.add(11);
c.clear();
System.out.println(c.isEmpty());
(12)toArray 能将集合对象转换成数组
for (int i = 0; i < 10; i++) {
c.add(i);
}
Object[] i= c.toArray();
System.out.println(Arrays.toString(i));
(13) c.toArray(数组) 将c的集合中的元素,装入到参数的数组中。
Integer[] array=new Integer[c.size()];
c.toArray(array);
System.out.println(Arrays.toString(array));
}
}
2.集合的遍历
(1)使用iterator接口。(原始方法)
(2)使用iterator接口。(1.8之后新增的方法)
(3)增强for循环
(4)forEach方法
(5)使用聚合操作
(1)使用iterator接口。(原始方法)
next获得下一个元素
hasnext 用来 判断是否还有下一个元素
remove 删除最后一个next元素:
public class Day13_2_Collection {
public static void main(String[] args) {
Collection<Integer> c=new ArrayList<Integer>();
c.add(10);
c.add(11);
c.add(12);
Iterator<Integer> it=c.iterator();//获得集合下的迭代器(迭代器是集合根本)
迭代器中 会存储集合中的元素 ,如果迭代器中的元素全部被访问完毕,再继续调用next会报错
可以看成迭代器是一次性。
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
每次在for循环的开头位置当便利完毕元素之后,重新创建一个迭代对象。
c.iterator()
for (Integer i : c) {
System.out.println(i);
}
for (Integer i : c) {
System.out.println(i);
}
while(it.hasNext()){
Integer ite=it.next();
System.out.println(ite);
}
remove:删除刚刚访问过 next()的元素 ,如果没有访问过,直接运行remove会报错
System.out.println(it.next());//10
it.remove();
System.out.println(c.contains(10));
编程的时候,需要注意的地方:
对于集合元素中进行迭代器的遍历时,不要同时对集合帝乡进行添加、删除元素(不要边遍历边删除、添加)会发生并行修改的异常。
while (it.hasNext()){
System.out.println(it.next());
c.remove(11);
(2)Iterator接口 1.8之后新增接口
public class Day13_2_Collection {
public static void main(String[] args) {
Collection<Integer> c=new ArrayList<Integer>();
c.add(10);
c.add(11);
c.add(12);
Iterator<Integer> it=c.iterator();
// it.forEachRemaining((Integer i)->System.out.println(i));
// it.forEachRemaining(i->System.out.println(i));
// it.forEachRemaining(System.out::println);
}
}
(3)增强for循环
(4)foreach方法
public class Day13_2_Collection {
public static void main(String[] args) {
Collection<Integer> c=new ArrayList<Integer>();
c.add(10);
c.add(11);
c.add(12);
c.forEach(System.out::println);
}
}
(5) 使用聚合操作
public class Day13_2_Collection {
public static void main(String[] args) {
Collection<Integer> c=new ArrayList<Integer>();
c.add(10);
c.add(11);
c.add(12);
c.stream().forEach(System.out::println);
}
}