一、线性表
线性表的特点:
(1)只有唯一一个首元素和末元素
(2)只有唯一一个前驱和后继
实现方式:顺序表示和链式表示
顺序表示:使用一组地址连续的存储单元依次存储线性表的元素
数组
特点:查找快,增删慢
时间复杂度:
查找:O(1)
增删:O(n)
链式表示:可以用任意的存储单元来存储数据,每个元素需要存储本身的信息外还要存储一个指示其后继的信息
链表
特点:查找慢,增删快
时间复杂度:
查找:O(n)
增删:O(1)
二、栈和队列
栈:先进后出
队列:先进先出
三、树和二叉树
树:结点拥有的子树称为结点的度,度为0的结点称为叶子,度不为0的结点称为分支。结点子树的根被称为结点的孩子,该结点被称为双亲。树中结点的最大层次被称为树的深度。
二叉树:树的每个结点至多只有两个子树
二叉树的性质:
(1)第i层有2^(i-1)个结点
(2)深度为k的二叉树至多有2^k-1个结点
(3)终端结点数=度为2的结点数+1
遍历方式:
前序遍历:中->左->右
中序遍历:左->中->右
后序遍历:左->右->中
示例:
最优二叉树(霍夫曼树)
从n个带权结点中构造出的带权路径最短的树被称为霍夫曼树
查找
折半查找
时间复杂度:O(log2n)
动态查找表
二叉排序树:左子树的所有结点小于根结点的值,右子树的所有结点大于根结点的值,左右子树也分别为二叉排序树。
中序遍历二叉排序树可以得到一个有序的序列。
哈希表
折半查找和二叉排序树都是需要通过比较来得到结果,而哈希表将关键字与存储位置建立了一个对应关系,使得不需要经过比较,一次存取便能得到结果,这个对应关系被称为哈希函数。
理想情况下哈希表的查找的时间复杂度为O(1)
冲突:计算哈希地址时可能会发现已存在相同的哈希地址,因此需要解决冲突。
解决冲突的方法有:
(1)开放定址法
(2)再哈希法
(3)链地址法
排序
直接插入排序
直接插入排序是一种最简单的排序方法,基本操作是将一个记录插入到已排好序的有序表中,从而得到一个新的、记录数增1的有序表。
时间复杂度:O(n^2)
实现代码:
public class Test1 {
public static void main(String[] args) {
int[] arr = {33,17,12,8,70,89,75,65,77,9};
//把第一个数字看成是有序的 ,所以从第二个数字开始循环。
for(int i=1;i<arr.length;i++) {
for (int j = i; j >0; j--) {
//将需要比较的数字从后向前依次与已排好序的数组中的数字比较,如果小于数组中的数字,那么发生交换
if(arr[j]<arr[j-1]) {
int temp = arr[j-1];
arr[j-1] = arr[j];
arr[j] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
}
简单选择排序
每次选择都从待排序的序列中找出最小值并放在待排序序列的第一位
时间复杂度:O(n^2)
实现代码:
public class Choose {
public static void main(String[] args) {
int[] arr = {33,17,12,8,70,89,75,65,77,9};
//n个数需要选择,i为已选出的个数,同时最小值要存在arr[i]中
for(int i=0;i<arr.length;i++){
int min=i; //保存最小值的索引,先假定为待排序序列中的第一个数
//此时把每轮选择序列的第一位当作最小值,由于有i个已选出的数,那么剩下需要比较的次数为n-i-1次
for(int j=i+1;j<arr.length;j++){
if(arr[min]>arr[j]){
min=j; //依次比较,直到找到最小值的索引
}
}
if(min!=i){ //将最小值与假定的最小值交换
int temp=arr[min];
arr[min]=arr[i];
arr[i]=temp;
}
}
System.out.println(Arrays.toString(arr));
}
}
冒泡排序
数组内的每两个数比较,使得每轮排序最大的数沉底,直至所有数字有序为止。
时间复杂度:O(n^2)
实现代码:
public class Maopao {
public static void main(String[] args) {
int[] arr = {33,17,12,8,70,89,75,65,77,9};
//n个数字,最多需要沉底n-1次
for(int i=0;i<arr.length-1;i++)
{
//已经有i个数字沉底,那么还有n-i个数字需要比较,则需要比较的次数为n-i-1
for(int k=1;k<arr.length-i;k++){
if(arr[k-1]>arr[k]){
int temp=arr[k];
arr[k]=arr[k-1];
arr[k-1]=temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
}
改进,加入flag位判断,如果已经有序就可以不用比较
public class Maopao {
public static void main(String[] args) {
int[] arr = {33,17,12,8,70,89,75,65,77,9};
//n个数字,最多需要沉底n-1次
int count=0;
for(int i=0;i<arr.length-1;i++)
{
boolean flag = false;
//已经有i个数字沉底,那么还有n-i个数字需要比较,则需要比较的次数为n-i-1
for(int k=1;k<arr.length-i;k++){
if(arr[k-1]>arr[k]){
int temp=arr[k];
arr[k]=arr[k-1];
arr[k-1]=temp;
flag=true;
}
}
if (!flag) {
break;
}
}
System.out.println(Arrays.toString(arr));
}
}
希尔排序
希尔排序是将整个待排记录序列分割成若干子序列进行直接插入排序,待整个序列中的记录“基本有序"时,再对记录进行一次直接插入排序。
快速排序
基本思想是通过一趟排序将待排记录分割成独立的两部分,其中一部分的记录的关键字均比另一部分关键字小,则可分别对这两个部分记录继续排序,使得整个序列有序。
代码实现:
public class QuickSort {
public static void quickSort(int[] arr,int low,int high){
int i,j,temp,t;
if(low>high){
return;
}
i=low;
j=high;
temp = arr[low];//temp为枢轴
while (i<j) {
//先看右边,从右到左依次与枢轴比较,如果小于枢轴就停止
while (temp<=arr[j]&&i<j) {
j--;
}
//再看左边,从左到右依次与枢轴比较,如果大于枢轴就停止
while (temp>=arr[i]&&i<j) {
i++;
}
//将满足条件的arr[i]和arr[j]交换
if (i<j) {
t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
//最后将枢轴与i和j相等位置的数字交换
arr[low] = arr[i];
arr[i] = temp;
//递归调用左半数组
quickSort(arr, low, j-1);
//递归调用右半数组
quickSort(arr, j+1, high);
}
public static void main(String[] args){
int[] arr = {33,17,12,8,70,89,75,65,77,9};
quickSort(arr, 0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
}
下面为第一次快排的示意