数据结构各自特点
数组Array
定义
在连续空间内存储一组相同类型的元素(同类数据元素的集合)
访搜插删
操作 | 表达式 | 复杂度 |
---|---|---|
数据访问(Access) | a[0] = 1 | o(1) |
数据搜索(Search) | 遍历 | o(N) |
插入数据(Insert) | 后移(没有空间全部搬家) | o(N) |
删除数据(Delete) | 前移 | o(N) |
特点
适合读不适合写 : 读多写少
常见操作
创建数组
int[] a = new int[n] ;
int[] a = {1,2,3};
int[] a = new int[3]{1,2,3};
ArrayList<String> arr = new ArrayList<>();
添加元素 o(N)
arr.add("abc");
访问元素o(1)
String A = a[2];
String arr1 = arr.get(1);
修改元素o(1)
a[2] = "de";
arr.set(1,"cd");
删除元素o(N)
arr.remove("cd"); //移除的是元素
arr.remove(2); //移除索引对应元素
//arr.remove((Integer) 5) 移除数元素
遍历o(N)
for (int i : a){
System.out.println(i);
}
for(String i = 0 ; i < arr.length; i++){
String num = arr.get(i);
}
for (String i : arr){
System.out.println(i);
}
查找元素o(N)
boolean arrture = arr.contains(3); //存在:true 不存在:false
数组长度o(1)
int length = a.length;
int arrlength = arr.size();
数组排序o(NlogN)(快速排序:分治法)
Arrays.sort(a); //排序升序
Collections.sort(arr); // small to big
System.out.println("排序升序:" + arr.toString());
Collections.reverse(arr);
System.out.println("排序降序:" + arr.toString());
链表LinkedList
定义
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的
访搜插删
操作 | 表达式 | 复杂度 |
---|---|---|
数据访问(Access) | 遍历 | o(N) |
数据搜索(Search) | 遍历 | o(N) |
插入数据(Insert) | list.add | o(1) |
删除数据(Delete) | list.remove | o(1) |
特点
适合写不适合读 : 读少写多
常见操作
创建列表
LinkedList<Integer> list = new LinkedList<>();
添加元素 o(1)、o(n)
list.add(1); //o(1)
list.add(2,99); //o(n) 遍历索引
访问元素o(N)
int listAccess = list.get(1);//o(n) 遍历索引
搜索元素 o(n)
//search
int listSearch = list.indexOf(10); //遍历才能找到元素,返回的是索引值 o(n)
修改元素o(n)
list.set(1,8);
删除元素o(N)
list.remove(2); //移除索引对应元素,遍历
//list.remove((Integer) 5) 移除数元素
遍历o(N)
for (int i : list){
System.out.println(i);
}
for (int i = 0 ; i < list.size() ; i++){
System.out.println(list.get(i));
}
查找元素o(N)
int listSearch = list.indexOf(10); //返回索引值
boolean listture = list.contains(3); //存在:true 不存在:false
数组长度o(1)
int listlength = list.size();
数组排序o(NlogN)(快速排序:分治法)
Collections.sort(list); // small to big
System.out.println("排序升序:" + list.toString());
Collections.reverse(list);
System.out.println("排序降序:" + list.toString());
节点
ListNode nodeSta = new ListNode(0); //创建首节点
ListNode nextNode; //声明一个变量在移动过程中指向当前节点
nextNode = nodeSta; //指向首节点
//创建链表
for (int i = 1 ; i < 10 ; i++){
ListNode node = new ListNode(i); //创建新节点
nextNode.next = node; //新节点相连
nextNode = nextNode.next;
System.out.println("节点:"+ nextNode.val);
}
队列Queue
定义
是一种先进先出的线性表。它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。
访搜插删
操作 | 表达式 | 复杂度 |
---|---|---|
数据访问(Access) | 队头 | o(1) |
数据搜索(Search) | 不可 | |
插入数据(Insert) | 队尾 | o(1) |
删除数据(Delete) | 即将出列元素 | o(1) |
特点
先进先出
常见操作
创建队列
Queue<Integer> queue = new LinkedList<>();
添加元素 o(1)
queue.add(1);
访问元素o(1)
int temp1 = queue.peek(); //获取即将出列元素
删除元素o(1)
int temp3 = queue.poll(); //删除即将出列的元素
遍历o(1)
while(!queue.isEmpty()){
int temp4 = queue.poll();
System.out.println(temp4);
} //遍历完删完
数组长度o(1)
int queuelength = queue.size();
栈Stack
定义
栈是一种操作受限的线性表只允许从一端插入和删除数据。栈有两种存储方式,即线性存储和链接存储(链表)
访搜插删
操作 | 表达式 | 复杂度 |
---|---|---|
数据访问(Access) | 栈顶 | o(1) |
数据搜索(Search) | 遍历 | o(N) |
插入数据(Insert) | 栈顶 | o(1) |
删除数据(Delete) | 栈顶 | o(1) |
特点
后入先出
常见操作
创建栈
Stack<Integer> stack = new Stack<>();
添加元素 o(1)
stack.push(1);
访问元素o(1)
int temp1 = stack.peek(); //获取即将出列元素
删除元素o(1)
int pop = stack.pop(); //删除即将出列的元素
遍历o(N)
while(!stack.isEmpty()){
System.out.println(stack.pop());
} //遍历完删完
数组长度o(1)
int size = stack.size();
哈希表HashTable
定义
是根据关键码值(Key value)而直接进行访问的数据结构
访搜插删
操作 | 表达式 | 复杂度 |
---|---|---|
数据访问(Access) | 不可 | |
数据搜索(Search) | key | o(1) |
插入数据(Insert) | 可能会发生碰撞 | o(1),发生碰撞为o(k) |
删除数据(Delete) | remove | o(1) |
特点
键对值
常见操作
创建哈希表
String[] hashTable = new String[4];
HashMap<Integer,String> map = new HashMap<>();
添加元素 o(1)
hashTable[1] = "abc";
map.put(1,"acd");
更新元素o(1)
hashTable[1] = "abc";
map.put(1,"acd");
删除元素o(1)
hashTable[1] = " ";
map.remove(1);
查找元素
boolean s = map.containKey(1); //存在键值
boolean sdf = map.containsValue("sdf");//存在值
遍历o(1)
for (Integer m : map.keySet()){
System.out.println(map.get(m));
}
for (String m : map.values()){
System.out.println(m);
}
Set<Map.Entry<Integer, String>> entries = map.entrySet();
for (Map.Entry<Integer, String> e : entries){
System.out.println(e);
}
//1=sdf
//2=saq
长度o(1)
int size = map.size();
哈希冲突
哈希冲突是指hash出来的地址被其他元素所占用;
解决的方法
JAVA为链地址法
解决的思路就是当出现冲突的时候将冲突的元素加入当前的链表之中
集合Set
定义
由一组无序且不重复的项组成的,根据哈希值计算储存位置,如冲突,JAVA加链表
访搜插删
操作 | 表达式 | 复杂度 |
---|---|---|
数据访问(Access) | 不可 | |
数据搜索(Search) | key | o(1) |
插入数据(Insert) | 可能会发生碰撞 | o(1),发生碰撞为o(k) |
删除数据(Delete) | remove | o(1) |
特点
不重复,无序
常见操作
创建集合
Set<Integer> s = new HashSet<>();
添加元素 o(1)
s.add(2);
删除元素o(1)
s.remove(1);
查找元素
boolean sdf = s.contains(1);//存在值
遍历o(1)
for (int a : s){
System.out.println(a);
}
长度o(1)
int size = s.size();
树Tree
树由n(n≥1)个有限节点组成一个具有层次关系的集合。每个节点有零个或多个子节点;没有父节点的节点称为根节点;每一个非根节点有且只有一个父节点。没有孩子的叫做叶子节点。
二叉树:每个节点最多含有两个子树的树称为二叉树;
满二叉树:叶节点除外的所有节点均含有两个子树的树被称为满二叉树;
完全二叉树:除最后一层外,所有层都是满节点,且最后一层缺右边连续节点的二叉树称为完全二叉树;
遍历:
前序遍历:根、左、右
中序遍历:左、根、右
后序遍历:左、右、根
TreeNode treeNode_6 = new TreeNode(6, null, null);
TreeNode treeNode_5 = new TreeNode(5, null, null);
TreeNode treeNode_4 = new TreeNode(4, null, null);
TreeNode treeNode_3 = new TreeNode(3, treeNode_6, null);
TreeNode treeNode_2 = new TreeNode(2, treeNode_4, treeNode_5);
TreeNode treeNode_1 = new TreeNode(1, treeNode_2, treeNode_3);
//确定二叉树的根节点
TreeNode root = treeNode_1;
//先序递归
preOrderTraverse(root);
System.out.println();
//先序 借助栈
preOrderByStack(root);
System.out.println();
//中序 递归
inOrderTraverse(root);
System.out.println();
//中序 栈
inOrderByStack(root);
System.out.println();
//后序 递归
afterOrderTraverse(root);
System.out.println();
//后序 栈
afterOrderByStack(root);
System.out.println();
}
//先/前序遍历,根-左-右 每个节点循环左右,直至null
public static void preOrderTraverse(TreeNode root) {
if (root != null) {
System.out.print(root.val + " ");
preOrderTraverse(root.left);
preOrderTraverse(root.right);
}
}
//先/前序遍历 非递归(借助栈)
public static void preOrderByStack(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<TreeNode>();
while (root != null || !stack.isEmpty()) { //当前节点存在 或 栈不为空
while (root != null) { //当前节点存在 左遍历树
System.out.print(root.val + " "); //打印当前节点
stack.push(root); //当前节点进栈
root = root.left; //进入其左子树
}
if (!stack.isEmpty()) { //如果栈不为空
root = stack.pop(); //栈顶节点出栈
root = root.right; //进入其右子树
}
}
}
//中序遍历
public static void inOrderTraverse(TreeNode root) {
if (root != null){
inOrderTraverse(root.left);
System.out.print(root.val + " ");
inOrderTraverse(root.right);
}
}
//中序遍历 非递归(借助栈)
public static void inOrderByStack(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<TreeNode>();
while (root != null || !stack.isEmpty()) { //当前节点存在 或 栈不为空
while (root != null) { //当前节点存在 左遍历树
stack.push(root); //当前节点进栈
root = root.left; //进入其左子树
}
root = stack.pop(); //栈顶节点出栈
System.out.print(root.val + " "); //打印当前节点
root = root.right; //进入其右子树
}
}
//后序遍历
public static void afterOrderTraverse(TreeNode root) {
if (root != null){
afterOrderTraverse(root.left);
afterOrderTraverse(root.right);
System.out.print(root.val + " ");
}
}
//后序遍历 非递归(借助栈)
public static void afterOrderByStack(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<TreeNode>();
TreeNode temp = null;
while (root != null || !stack.isEmpty()) { //当前节点存在 或 栈不为空
while (root != null) { //当前节点存在 左遍历树
stack.push(root); //当前节点进栈
root = root.left; //进入其左子树
}
if (!stack.isEmpty()){ //栈不为空
root = stack.peek(); //取栈顶元素,但栈顶元素不出栈
//当前节点的右子树为空,或者 其右子树刚访问过
if ((root.right== null) || (root.right == temp)){
root = stack.pop(); //栈顶节点出栈
System.out.print(root.val + " "); //打印当前节点
temp = root; //将当前节点赋值给temp
root = null; //将当前节点置空
}else {
root = root.right; //进入其右子树
}
}
}
}
TreeSet
会进行自动排序,升序
TreeSet<Integer> t = new TreeSet<>();
t.add(4);
t.add(30);
t.add(90);
t.add(89);
System.out.println(t);
//[4, 30, 89, 90]
按照构造方法进行排序,利用比较器
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getAge() - s2.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()):num;
return num2;
}
});
s1 - s2 (升序)--------------------------: s2 - s1(降序)
s2.compareTo(s1)(a在后,升序)-: s1.compareTo(s2)(a在前:降序)
堆
定义:
堆(heap)也被称为优先队列(priority queue)。队列中允许的操作是先进先出(FIFO),在队尾插入元素,在队头取出元素。而堆也是一样,在堆底插入元素,在堆顶取出元素,但是堆中元素的排列不是按照到来的先后顺序,而是按照一定的优先顺序排列的。
是一种二叉树结构,完全二叉树
每个节点大于孩子节点: 最大堆,堆顶为最大值
每个节点小于孩子节点: 最小堆,堆顶为最小值
堆的时间复杂度:
无序的数转化为完全二叉树:o(n)
完全二叉树准换为堆,进行大小比较:
深度:h 节点数:N 当前层交换次数:C
h = 0 : N = (n+1)/2 , C= 0 ;
h = 1: N = (n+1)/4 , C=(n+1)/4 * 1 ;
h = 2: N = (n+1)/8 , C=(n+1)/8 * 2 ;
…
C=(n+1)/2^(h+1) * h
C的和:n/2* 和(i/2^i) = n/2*2(收敛于2)
o(n)
时间复杂度取决于树的高度h。而完全二叉树的树的高度为[logn]向下取整
h = logn
访搜插删
操作 | 表达式 | 复杂度 |
---|---|---|
数据访问(Access) | 不可 | |
数据搜索(Search) | o(1) | |
插入数据(Insert) | 队尾 | o(logn) |
删除数据(Delete) | 即将出列元素 | o(logn) |
特点
排序,出堆顶
常见操作
创建队列
//create a min heap
PriorityQueue<Integer> minHeap = new PriorityQueue<>(); //优先级队列
//create a max heap
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
添加元素 o(logn)
minHeap.add(10);
minHeap.add(8);
minHeap.add(9);
minHeap.add(11);
minHeap.add(2);
获取元素o(1)
minHeap.peek(); //获取堆顶元素
删除元素o(logn)
int temp3 = minHeap.poll(); //删除堆顶元素
遍历o(n)
while(!queue.isEmpty()){
int temp4 = minHeap.poll();
System.out.println(temp4);
} //遍历完删完
数组长度o(1)
int length = minHeap.size();