插入排序
简单插入排序算法原理:从整个待排序列中选出一个元素插入到已经有序的子序列中去,得到一个有序的、元素加一的子序列,直到整个序列的待插入元素为0,则整个序列全部有序。
在实际的算法中,我们经常选择序列的第一个元素作为有序序列(因为一个元素肯定是有序的),我们逐渐将后面的元素插入到前面的有序序列中,直到整个序列有序.。
插入排序(Insertion Sort)是一种简单有效的比较排序算法,属于原地排序。其核心思想是:在每次迭代过程中从输入序列中取出一个元素插入到一个有序序列中,形成新的有序序列。重复该过程,直到序列中所有元素都被取出。
解析:
输入序列:就是要排序的一组元素 现在我把它称为N。
有序序列:输入序列的一部分,该部分是有序的(升序或降序) 称为M。
如何插入?: 从“N”中的未排序部分取出一个元素“K”和“M”中的元素比较,插入到正确的位置,该元素“K”和“M”组合形成一个新的有序序列(形成新的“M”)。
图解:
例如:给定一个序列:6 8 1 4 5 3 7 2,按升序排列
6 8 1 4 5 3 7 2 (考虑索引位置0~1) 注意:这时认为6是"M",8将插入到"M"中
第一次排序: 6 8 1 4 5 3 7 2 (考虑索引位置0~2) 这时认为序列(6 8)是"M",1将插入到"M"中
第二次排序: 1 6 8 4 5 3 7 2 (考虑索引位置0~3)
第三次排序: 1 4 6 8 5 3 7 2 重复以上过程,直到序列被排序完成
第四次排序: 1 4 5 6 8 3 7 2
第五次排序: 1 3 4 5 6 8 7 2
第六次排序: 1 3 4 5 6 7 8 2
第七次排序: 1 2 3 4 5 6 7 8 (已排序的序列!)
优点:
从上图分析中我们能得出:
数据量较少时效率高。
适应性(adaptive):如果输入序列已 预排序(序列中一部分是有序的,例如:图解序列中的6 8两个元素,是有序的),则时间复杂度为O(n+d),d是反转次数(交换次数)。
算法的实际运行效率优于选择排序和冒泡排序。这一点是由于它的适应性。
static void insertionSort(int[] array){
int n = array.length;
int i,j,v;
for (i = 1; i < n; i++) {
/**
* v = array[i]: 表示将要插入到已经排序好的序列中的元素
* 例如:array[2] 将要插入到array[0],array[1]这个有序序列中
/
v = array[i];
j = i;
while(j>=1 && array[j-1]>v){
//为将要插入的新元素腾出空间。
array[j] = array[j-1];
j–;
}
//将新元素插入到已排序序列中。
array[j] = v;
/*
* 这是为了显示排序结果,自创建的方法,封装在SortUtil中
/
SortUtil.restOfEach(array, i);
}
/*
* 这是为了显示排序结果,自创建的方法,封装在SortUtil中
*/
SortUtil.afterSort(array);
}
BST(二叉搜索树)
BST(Binary Search Tree)目的是为了提高查找的性能,其查找在平均和最坏的情况下都是logn级别,接近二分查找。
其特点是:每个节点的值大于其任意左侧子节点的值,小于其任意右节点的值。
搜索树数据结构支持许多动态集合操作,包括SEARCH(查找指定结点)、MINIMUM(最小关键字结点)、MAXMUM(最大关键字结点)、PREDECESSOR(结点的先驱)、SUCCESSOR(结点的后继)、INSERT(结点的插入)和DELETE(结点的删除)等。因此,我们使用一棵搜索树既可以作为一个字典又可以作为一个优先队列。
二叉搜索树上的基本操作所花费的时间与这棵树的高度成正比。对于有n个结点的一棵完全二叉树来说,这些操作的最坏运行时间为Θ(lgn) \Theta(\lg n)Θ(lgn)。然而,如果这棵树是一条n 个结点组成的线性链,那么同样的操作就要花费Θ(n) \Theta(n)Θ(n)的最坏运行时间。当然,我们可以通过随即构造一棵二叉搜索树的期望高度为O(nlgn) O(n \lg n)O(nlgn),因此这样一棵的动态集合的基本操作的平均运行时间是Θ(lgn) \Theta(\lg n)Θ(lgn)。
基本概念
顾名思义,二叉搜索树是一棵二叉树,如图所示。这样一棵树可以使用一个链表结构表示,其中每个结点就是一个对象。除了结点中的关键字外,每个结点还包含属性left、right、p left、right、pleft、right、p,它们分别指向结点的左孩子、右孩子和双亲。如果某个孩子结点和父结点不存在,则相应属性的值为null nullnull。根结点是树中唯一父结点为null nullnull的结点。
二叉搜索树中的关键字总数以满足二叉搜索树性质的方式来存储:设x xx是二叉搜索树中的一个结点。如果y yy是x xx左子树中的一个结点,那么y.key≤x.key y.key \le x.keyy.key≤x.key;如果y yy是x xx右子树中的一个结点,那么y.key≥x.key y.key \ge x.keyy.key≥x.key。这一性质,我们从上图中不难看出。树根的关键字为6,在其左子树中右关键字为2,5,它们均不大于6;在其右子树中有关键字7和8,它们均不大于6。这个性质对树中的每个结点都成立
二叉搜索树的这一性质允许我们通过一个递归算法来按序输出二叉搜索树中的所有关键字。这种算法称为中序遍历。这样命名的原因是输出的子树根的关键字在其左右子树的关键字之间(类似地,先序遍历中的根的关键字在其左右子树的关键字之前,而后序遍历中的根的关键字在其左右子树的关键字之后)。
1、定义
二叉搜索树(BST)又称二叉查找树或二叉排序树。一棵二叉搜索树是以二叉树来组织的,可以使用一个链表数据结构来表示,其中每一个结点就是一个对象。一般地,除了key和卫星数据(文末附注1)之外,每个结点还包含属性lchild、rchild和parent,分别指向结点的左孩子、右孩子和双亲(父结点)。如果某个孩子结点或父结点不存在,则相应属性的值为空(NIL)。根结点是树中唯一父指针为NIL的结点,而叶子结点的孩子结点指针也为NIL。
2、基本性质
根据《算法导论》(中文第3版)的相关介绍,二叉搜索树中的关键字总是以满足二叉搜索树性质的方式来存储:
设x是二叉搜索树中的一个结点。如果y是x左子树中的一个结点,那么y.key≤x.key。如果y是x右子树中的一个结点,那么y.key≥x.key。
在二叉搜索树中:
① 若任意结点的左子树不空,则左子树上所有结点的值均不大于它的根结点的值;
② 若任意结点的右子树不空,则右子树上所有结点的值均不小于它的根结点的值;
③ 任意结点的左、右子树也分别为二叉搜索树。
二叉树可以干什么?
排序有快速排序,归并排序,查找有二分法,甚至直接遍历查找,二叉树的使用很少。
实际场景使用上,用的最多的是二叉平衡树,有种特殊的二叉平衡树就是红黑树,Java集合中的TreeSet和TreeMap,C++STL中的set,map以及LInux虚拟内存的管理,都是通过红黑树去实现的,还有哈弗曼树编码方面的应用,以及B-Tree,B+-Tree在文件系统中的应用。当然二叉查找树可以用来查找和排序。
二叉树的分类
满二叉树:从高到低,除了叶节点外,所以节点左右节点都存在。
完全二叉树:比满二叉树少几个叶节点,从左向右放子节点。
平衡二叉树:空树或者它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树也都是平衡树。
二叉搜索树:空树或者二叉树的所有节点比他的左子节点大,比他的右子节点小。
红黑树:不仅是具有二叉搜索树的属性,还具有平衡树的属性,有序且子树差不超过1,颜色规则:根节点和特殊节点(即叶节点下面两个虚无的节点和未填写的节点)是黑的,红节点的左右子节点是黑的,最重要的是对于每个节点,从该节点到子孙叶节点的所有路径包含相同数目的黑节点。
二叉树在搜索上的优势
数组的搜索比较方便,可以直接使用下标,但删除或者插入就比较麻烦了,而链表与之相反,删除和插入都比较简单,但是查找很慢,这自然也与这两种数据结构的存储方式有关,数组是取一段相连的空间,而链表是每创建一个节点便取一个节点所需的空间,只是使用指针进行连接,空间上并不是连续的。而二叉树就既有链表的好处,又有数组的优点。
DDL(Data Definition Language) 数据库定义语言
- 基本操作
查看所有数据库名称:【语法:show databases;】
切换数据库:【语法:use test;】,切换到test数据库;
显示表:【语法:show tables;】
查询表:【语法:select * from goods;】
2.操作数据库
2.1 创建数据库:
语法:
create database test999;
细粒度:如果不存在创建,存在就不创建!
create database if not exists test999;
例如:
create database test999;
创建一个名为 test999的数据库。如果这个数据已经存在,那么会报错。
例如:
create database if not exists test999;
在名为 test999的数据库不存在时创建该库,这样可以避免报错。
2.2 删除数据库:
语法:
drop database test999;
细粒度:如果存在删除,不存在就不删除!
drop database if exists test999;
例如:
drop database test999;
删除名为test999的数据库。如果这个数据库不存在,那么会报错。
drop database if existstest999;
- 就算test999不存在,也不会的报错。
2.3 修改数据库编码:
语法:
alter database test999 character set utf8;
修改数据库test999的编码为utf8。注意,在MySQL中所有的UTF-8编码都不能使用中间的“-”,即UTF-8要书写为UTF8。
- 数据类型
MySQL与Java一样,也有数据类型。MySQL中数据类型主要应用在列上。
3.1 常用类型:
- int:整型
- double:浮点型,例如double(5,2)表示最多5位,其中必须有2位小数,即最大值为999.99;
- decimal:泛型型(浮点型),在表单钱方面使用该类型,因为不会出现精度缺失问题;
- char:固定长度字符串类型;长度默认255。
- varchar:可变长度字符串类型;(65535)
- text:字符串类型;
- blob:字节类型;
- date:日期类型,格式为:yyyy-MM-dd;
- time:时间类型,格式为:hh:mm:ss
- timestamp:时间戳类型;yyyy-MM-dd hh:mm:ss
4.操作表
4.1 创建表:
【语法:】
CREATE TABLE 表名(
列名 列类型,
列名 列类型,
……
);
例如:创建一个stu表
mysql> create table stu(
-> sid char(6),
-> sname varchar(20),
-> age int,
-> gender varchar(10)
-> );
4.2查看当前数据库中所有表名称:
show tables;
4.3查看表结构:
desc test999;
4.4删除表:
dorp table test999;
4.5修改表:
- 修改之添加列:给stu表添加classname列:
mysql> alter table stu add(
-> classname varchar(100)
-> );
修改之修改列类型:修改stu表的gender列类型为char(2):
mysql> alter table stu
-> modify gender char(2);
修改之修改列名:修改stu表的gender列名为sex:
mysql> alter table stu
-> change gender sex char(2);
修改之删除列:删除stu表的classname列:
mysql> alter table stu
-> drop classname;
修改之修改表名称:修改stu表名称为student:
mysql> alter table stu
-> rename to student;
代理模式
代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
为其他的对象提供代理以控制这个对象的访问
举个例子来说明:假如说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了。
代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象.
代理模式的作用:aop实现、拦截器、中介、媒婆、解耦、专人做专事
spring的体现: Spring 的 Proxy 模式在 AOP 中有体现, 比如 JdkDynamicAopProxy和 Cglib2AopProxy
JDK动态代理:优点是,生成动态代理对象的速度快,适合频繁创建代理对象的程序;
缺点是,执行速度慢,且不能为未实现接口的类创建代理。
CGLIB动态代理:优点是,执行速度快,且能够为未实现接口的类创建代理;
缺点是,不能代理final修饰的方法,且生成代理对象的速度慢,不适合频繁创建代理对象的程序。
在Spring中,默认使用JDK动态代理,如果目标对象没有实现接口,会转而使用CGLIB动态代理。