1、数组转字符串
实现一个方法 toString, 把一个整型数组转换成字符串. 例如数组 {1, 2, 3} , 返回的字符串为 "[1, 2, 3]", 注意 逗号 的位置和数量
4、26. 删除有序数组中的重复项 - 力扣(LeetCode)
6、实现 ArrayList 类
public class MyArraylist {
public int[] elem;
public int usedSize;//0
//默认容量
private static final int DEFAULT_SIZE = 10;
public MyArraylist() {
this.elem = new int[DEFAULT_SIZE];
}
/**
* 打印顺序表:
* 根据usedSize判断即可
*/
public void display() {
}
// 新增元素,默认在数组最后新增
public void add(int data) {
}
/**
* 判断当前的顺序表是不是满的!
* @return true:满 false代表空
*/
public boolean isFull() {
}
private boolean checkPosInAdd(int pos) {
return true;//合法
}
// 在 pos 位置新增元素
public void add(int pos, int data) {
}
// 判定是否包含某个元素
public boolean contains(int toFind) {
return false;
}
// 查找某个元素对应的位置
public int indexOf(int toFind) {
return -1;
}
// 获取 pos 位置的元素
public int get(int pos) {
}
private boolean isEmpty() {
}
// 给 pos 位置的元素设为【更新为】 value
public void set(int pos, int value) {
}
/**
* 删除第一次出现的关键字key
* @param key
*/
public void remove(int key) {
}
// 获取顺序表长度
public int size() {
}
// 清空顺序表
public void clear() {
}
public class MyArraylist {
public int[] elem;
public int usedSize;//0
//默认容量
private static final int DEFAULT_SIZE = 10;
public MyArraylist() {
this.elem = new int[DEFAULT_SIZE];
}
/**
* 打印顺序表:
* 根据usedSize判断即可
*/
public void display() {
//usedSize==0
for (int i = 0; i < this.usedSize; i++) {
System.out.print(this.elem[i]+" ");
}
System.out.println();
}
// 新增元素,默认在数组最后新增
public void add(int data) {
//1、判断是否是满的,如果满的,那么进行扩容
if(isFull()) {
//扩容
this.elem = Arrays.copyOf(this.elem,2*this.elem.length);
}
//2、不满进行插入
this.elem[this.usedSize] = data;
this.usedSize++;
}
/**
* 判断当前的顺序表是不是满的!
* @return true:满 false代表空
*/
public boolean isFull() {
/*if(this.usedSize == this.elem.length) {
return true;
}
return false;*/
return this.usedSize == this.elem.length;
}
private boolean checkPosInAdd(int pos) {
if(pos < 0 || pos > this.usedSize) {
System.out.println("pos位置不合法");
return false;
}
return true;//合法
}
// 在 pos 位置新增元素
public void add(int pos, int data) {
//判断下标是否是合法的
if(!checkPosInAdd(pos)) {
throw new MyArrayListIndexOutOfException("添加方法的pos不合理!");
}
//判断是否是满的
if (isFull()) {
this.elem = Arrays.copyOf(this.elem,2*this.elem.length);
}
//挪数据
for (int i = this.usedSize-1; i >= pos ; i--) {
this.elem[i+1] = this.elem[i];
}
//挪完了数据
this.elem[pos] = data;
this.usedSize++;
}
// 判定是否包含某个元素
public boolean contains(int toFind) {
for (int i = 0; i < this.usedSize; i++) {
if(this.elem[i] == toFind) {
return true;
}
}
return false;
}
// 查找某个元素对应的位置
public int indexOf(int toFind) {
for (int i = 0; i < this.usedSize; i++) {
if(this.elem[i] == toFind) {
return i;
}
}
return -1;
}
private boolean checkPosInGet(int pos) {
if(pos < 0 || pos >= this.usedSize) {
System.out.println("pos位置不合法");
return false;
}
return true;//合法
}
// 获取 pos 位置的元素
public int get(int pos) {
if(!checkPosInGet(pos)) {
throw new MyArrayListIndexOutOfException("获取pos下标时,位置不合法");
}
//不用写判断空不空 没有问题的
if(isEmpty()) {
throw new MyArrayListEmptyException("获取元素的时候,顺序表为空!");
}
return this.elem[pos];
}
private boolean isEmpty() {
return this.usedSize == 0;
}
// 给 pos 位置的元素设为【更新为】 value
public void set(int pos, int value) {
if(!checkPosInGet(pos)){
throw new MyArrayListIndexOutOfException("更新pos下标的元素,位置不合法");
}
//如果合法 ,那么其实不用判断顺序表为空的状态了
if(isEmpty()) {
throw new MyArrayListEmptyException("顺序表为空!");
}
//顺序表为满的情况也可以更新
this.elem[pos] = value;
}
/**
* 删除第一次出现的关键字key
* @param key
*/
public void remove(int key) {
if(isEmpty()) {
throw new MyArrayListEmptyException("顺序表为空,不能删除!");
}
int index = indexOf(key);
if(index == -1) {
System.out.println("不存在你要删除的数据");
return;
}
for (int i = index; i < this.usedSize-1; i++) {
this.elem[i] = this.elem[i+1];
}
//删除完成
this.usedSize--;
// this.elem[usedSize] = null; 如果是引用类型 这里需要置为空
}
// 获取顺序表长度
public int size() {
return this.usedSize;
}
// 清空顺序表
public void clear() {
/*
如果是引用数据类型 得一个一个置为空 这样做才是最合适的
for (int i = 0; i < this.usedSize; i++) {
this.elem[i] = null;
}
this.usedSize = 0;
*/
this.usedSize = 0;
}
7、实现无头单链表的基本操作
// 1、无头单向非循环链表实现
public class SingleLinkedList {
//头插法
public void addFirst(int data);
//尾插法
public void addLast(int data);
//任意位置插入,第一个数据节点为0号下标
public boolean addIndex(int index,int data);
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key);
//删除第一次出现关键字为key的节点
public void remove(int key);
//删除所有值为key的节点
public void removeAllKey(int key);
//得到单链表的长度
public int size();
public void display();
public void clear();
}
public class SingleLinkedList{
static class ListNode {
public int val;//数值域public ListNode next;//存储下一个节点的地址
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;//代表单链表的头结点的引用
/**
* 这里只是简单的进行,链表的构造。
*/public void createList() {
ListNode listNode1 = new ListNode(12);
ListNode listNode2 = new ListNode(23);
ListNode listNode3 = new ListNode(34);
ListNode listNode4 = new ListNode(45);
ListNode listNode5 = new ListNode(56);
listNode1.next = listNode2;
listNode2.next = listNode3;
listNode3.next = listNode4;
listNode4.next = listNode5;
this.head = listNode1;
}
public void display() {
ListNode cur = head;
while (cur != null) {
System.out.print(cur.val+" ");
cur = cur.next;
}
System.out.println();
}
/**
* 从指定的节点开始答应
* @param newHead
*/public void display(ListNode newHead) {
ListNode cur = newHead;
while (cur != null) {
System.out.print(cur.val+" ");
cur = cur.next;
}
System.out.println();
}
/**
* 头插法
* OaddLast
*/public void addFirst(int data){
ListNode node = new ListNode(data);
/*if(this.head == null) {
this.head = node;
}else {
node.next = this.head;
this.head = node;
}*/
node.next = this.head;
this.head = node;
}
//尾插法 O(n)public void addLast(int data) {
ListNode node = new ListNode(data);
if(head == null) {
head = node;
}else {
ListNode cur = head;
while (cur.next != null) {
cur = cur.next;
}
//cur.next == null;
cur.next = node;
}
}
/**
* 任意位置插入,第一个数据节点为0号下标
* 指定位置插入
*/
public void addIndex(int index,int data) throws MySingleListIndexOutOfException{
checkIndexAdd(index);
if(index == 0) {
addFirst(data);
return;
}
if(index == size()) {
addLast(data);
return;
}
ListNode node = new ListNode(data);
ListNode cur = findIndexSubOne(index);
node.next = cur.next;
cur.next = node;
}
/**
* 找到index-1位置的节点
* @param index
* @return 该节点的地址
*/private ListNode findIndexSubOne(int index) {
ListNode cur = this.head;
while (index-1 != 0) {
cur = cur.next;
index--;
}
return cur;
}
private void checkIndexAdd(int index) {
if(index < 0 || index > size()) {
throw new MySingleListIndexOutOfException("任意位置插入的时候,index不合法!");
}
}
//查找是否包含关键字key是否在单链表当中 314public boolean contains(int key) {
//head == null
ListNode cur = this.head;
//cur != null 说明 没有遍历完 链表while (cur != null) {
if(cur.val == key) {
return true;
}
cur = cur.next;
}
return false;
}
//删除第一次出现关键字为key的节点public void remove(int key){
if(this.head == null) {
System.out.println("此时链表为空,不能进行删除!");
return;
}
if(this.head.val == key) {
//判断 第一个节点是不是等于我要删除的节点this.head = this.head.next;
return;
}
ListNode cur = this.head;
while (cur.next != null) {
if(cur.next.val == key) {
//进行删除了
ListNode del = cur.next;
cur.next = del.next;
return;
}
cur = cur.next;
}
}
//删除所有值为key的节点public void removeAllKey(int key){
if(this.head == null) {
return;
}
ListNode cur = this.head.next;
ListNode prev = this.head;
while (cur != null) {
if(cur.val == key) {
prev.next = cur.next;
cur = cur.next;
}else {
prev = cur;
cur = cur.next;
}
}
//单独处理了头节点if(this.head.val == key) {
head = head.next;
}
}
//得到单链表的长度public int size(){
int count = 0;
ListNode cur = this.head;
while (cur != null) {
count++;
cur = cur.next;
}
return count;
}
/**
* 当我们 执行clear这个函数的时候,会将这个链表当中的所有的节点回收
*/public void clear(){
//this.head = null;//这种做法 比较粗暴!
ListNode cur = this.head;
ListNode curNext = null;
while (cur != null) {
curNext = cur.next;
cur.next = null;
cur = curNext;
}
head = null;
}
}
10、876. 链表的中间结点 - 力扣(LeetCode)
11、链表中倒数第k个结点_牛客题霸_牛客网 (nowcoder.com)
12、模拟实现 LinkedList 类
// 2、无头双向链表实现
public class LinkedList {
//头插法
public void addFirst(int data);
//尾插法
public void addLast(int data);
//任意位置插入,第一个数据节点为0号下标
public boolean addIndex(int index,int data);
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key);
//删除第一次出现关键字为key的节点
public void remove(int key);
//删除所有值为key的节点
public void removeAllKey(int key);
//得到单链表的长度
public int size();
public void display();
public void clear();
}
public class MyLinkedList {
static class ListNode {
public int val;
public ListNode prev;//前驱
public ListNode next;//后继
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;//标记头
public ListNode last;//标记尾巴
MyLinkedList() {
//last = head = new ListNode(-1);
}
public void display(){
ListNode cur = head;
while (cur != null) {
System.out.print(cur.val+" ");
cur = cur.next;
}
System.out.println();
}
//头插法
public void addFirst(int data){
ListNode node = new ListNode(data);
if(head == null) {
head = node;
last = node;
}else {
node.next = head;
head.prev = node;
head = node;
}
}
//尾插法
public void addLast(int data){
ListNode node = new ListNode(data);
if(head == null) {
head = node;
last = node;
}else {
last.next = node;
node.prev = last;
last = node;
}
}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data){
if(index < 0 || index > size()) {
System.out.println("index不合法!");
return;
}
if(index == 0) {
addFirst(data);
return;
}
if(index == size()) {
addLast(data);
return;
}
//cur拿到了index下标的节点的地址
ListNode cur = searchIndex(index);
ListNode node = new ListNode(data);
node.next = cur;
cur.prev.next = node;
node.prev = cur.prev;
cur.prev = node;
}
private ListNode searchIndex(int index) {
ListNode cur = head;
while (index != 0) {
cur = cur.next;
index--;
}
return cur;
}
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key){
ListNode cur = head;
while (cur != null) {
if(cur.val == key) {
return true;
}
cur = cur.next;
}
return false;
}
//删除第一次出现关键字为key的节点
public void remove(int key){
ListNode cur = head;
while (cur != null) {
if(cur.val == key) {
//判断当前是不是头节点
if(cur == head) {
head = head.next;
if(head != null) {
head.prev = null;//这里有问题!!!! 一个节点!
}
}else {
//中间和尾巴的情况
cur.prev.next = cur.next;
if(cur.next != null) {
cur.next.prev = cur.prev;
}else {
last = last.prev;
}
}
return;//删完走人
}else {
cur = cur.next;
}
}
}
//删除所有值为key的节点 之遍历链表一遍
public void removeAllKey(int key){
ListNode cur = head;
while (cur != null) {
if(cur.val == key) {
//判断当前是不是头节点
if(cur == head) {
head = head.next;
if(head != null) {
head.prev = null;//这里有问题!!!! 一个节点!
}
}else {
//中间和尾巴的情况
cur.prev.next = cur.next;
if(cur.next != null) {
cur.next.prev = cur.prev;
}else {
last = last.prev;
}
}
cur = cur.next;
}else {
cur = cur.next;
}
}
}
//得到单链表的长度
public int size(){
int count = 0;
ListNode cur = head;
while (cur != null) {
count++;
cur = cur.next;
}
return count;
}
public void clear(){
ListNode cur = head;
while (cur != null) {
ListNode curNext = cur.next;
///cur.val = null;
cur.prev = null;
cur.next = null;
cur = curNext;
}
head = null;
last = null;
}
}
14、142. 环形链表 II - 力扣(LeetCode)
16、链表的回文结构_牛客题霸_牛客网 (nowcoder.com)
17、链表分割_牛客题霸_牛客网 (nowcoder.com)
18、21. 合并两个有序链表 - 力扣(LeetCode)
20、栈的压入、弹出序列_牛客题霸_牛客网 (nowcoder.com)
21、150. 逆波兰表达式求值 - 力扣(LeetCode)
23、225. 用队列实现栈 题解 - 力扣(LeetCode)
26、实现二叉树的基本操作
public class BinaryTree {
static class TreeNode {
public char val;
public TreeNode left;//左孩子的引用
public TreeNode right;//右孩子的引用
public TreeNode(char val) {
this.val = val;
}
}
/**
* 创建一棵二叉树 返回这棵树的根节点
*
* @return
*/
public TreeNode createTree() {
}
// 前序遍历
public void preOrder(TreeNode root) {
}
// 中序遍历
void inOrder(TreeNode root) {
}
// 后序遍历
void postOrder(TreeNode root) {
}
public static int nodeSize;
/**
* 获取树中节点的个数:遍历思路
*/
void size(TreeNode root) {
}
/**
* 获取节点的个数:子问题的思路
*
* @param root
* @return
*/
int size2(TreeNode root) {
}
/*
获取叶子节点的个数:遍历思路
*/
public static int leafSize = 0;
void getLeafNodeCount1(TreeNode root) {
}
/*
获取叶子节点的个数:子问题
*/
int getLeafNodeCount2(TreeNode root) {
}
/*
获取第K层节点的个数
*/
int getKLevelNodeCount(TreeNode root, int k) {
}
/*
获取二叉树的高度
时间复杂度:O(N)
*/
int getHeight(TreeNode root) {
}
// 检测值为value的元素是否存在
TreeNode find(TreeNode root, char val) {
return null;
}
//层序遍历
void levelOrder(TreeNode root) {
}
// 判断一棵树是不是完全二叉树
boolean isCompleteTree(TreeNode root) {
return true;
}
}
public class BinaryTree {
static class TreeNode {
public char val;
public TreeNode left;//左孩子的引用
public TreeNode right;//右孩子的引用
public TreeNode(char val) {
this.val = val;
}
}
/**
* 创建一棵二叉树 返回这棵树的根节点
* 暂且使用穷举的方式创建二叉树
* @return
*/
public TreeNode createTree() {
TreeNode A = new TreeNode('A');
TreeNode B = new TreeNode('B');
TreeNode C = new TreeNode('C');
TreeNode D = new TreeNode('D');
TreeNode E = new TreeNode('E');
TreeNode F = new TreeNode('F');
TreeNode G = new TreeNode('G');
TreeNode H = new TreeNode('H');
A.left = B;
A.right = C;
B.left = D;
B.right = E;
C.left = F;
C.right = G;
//E.right = H;
return A;
}
// 前序遍历
public void preOrder(TreeNode root) {
if (root == null) return;
System.out.print(root.val + " ");
preOrder(root.left);
preOrder(root.right);
}
// 中序遍历
void inOrder(TreeNode root) {
if (root == null) return;
inOrder(root.left);
System.out.print(root.val + " ");
inOrder(root.right);
}
// 后序遍历
void postOrder(TreeNode root) {
if (root == null) return;
postOrder(root.left);
postOrder(root.right);
System.out.print(root.val + " ");
}
public static int nodeSize;
/**
* 获取树中节点的个数:遍历思路
*/
void size(TreeNode root) {
if (root == null) return;
nodeSize++;
size(root.left);
size(root.right);
}
/**
* 获取节点的个数:子问题的思路
*
* @param root
* @return
*/int size2(TreeNode root) {
if (root == null) return 0;
return size2(root.left)
+ size2(root.right) + 1;
}
/*
获取叶子节点的个数:遍历思路
*/
public static int leafSize = 0;
void getLeafNodeCount1(TreeNode root) {
if (root == null) return;
if (root.left == null && root.right == null) {
leafSize++;
}
getLeafNodeCount1(root.left);
getLeafNodeCount1(root.right);
}
/*
获取叶子节点的个数:子问题
*/int getLeafNodeCount2(TreeNode root) {
if (root == null) return 0;
if (root.left == null && root.right == null) {
return 1;
}
return getLeafNodeCount2(root.left) + getLeafNodeCount2(root.right);
}
/*
获取第K层节点的个数
*/int getKLevelNodeCount(TreeNode root, int k) {
if (root == null) return 0;
if (k == 1) return 1;
return getKLevelNodeCount(root.left, k - 1) +
getKLevelNodeCount(root.right, k - 1);
}
/*
获取二叉树的高度
时间复杂度:O(N)
*/int getHeight(TreeNode root) {
if (root == null) return 0;
int leftH = getHeight(root.left);
int rightH = getHeight(root.right);
return leftH > rightH ? leftH + 1 : rightH + 1;
}
// 检测值为value的元素是否存在
TreeNode find(TreeNode root, char val) {
if (root == null) return null;
if (root.val == val) return root;
TreeNode ret = find(root.left, val);
if (ret != null) {
return ret;
}
ret = find(root.right, val);
if (ret != null) {
return ret;
}
return null;
}
//层序遍历
void levelOrder(TreeNode root) {
if (root == null) return;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode cur = queue.poll();
System.out.print(cur.val + " ");
if (cur.left != null) {
queue.offer(cur.left);
}
if (cur.right != null) {
queue.offer(cur.right);
}
}
}
// 判断一棵树是不是完全二叉树
boolean isCompleteTree(TreeNode root) {
if (root == null) return false;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode cur = queue.poll();
if (cur != null) {
queue.offer(cur.left);
queue.offer(cur.right);
} else {
break;
}
}
//第2次遍历队列 判断队列当中 是否存在非空的元素while (!queue.isEmpty()) {
TreeNode cur = queue.peek();
if (cur == null) {
queue.poll();
} else {
return false;
}
}
return true;
}
}
27、二叉树遍历_牛客题霸_牛客网 (nowcoder.com)
29、572. 另一棵树的子树 - 力扣(LeetCode)
33、144. 二叉树的前序遍历 - 力扣(LeetCode)
34、106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)
35、105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)
36、236. 二叉树的最近公共祖先 - 力扣(LeetCode)
37、107. 二叉树的层序遍历 II - 力扣(LeetCode)
38、102. 二叉树的层序遍历 - 力扣(LeetCode)
39、面试题 17.14. 最小K个数 - 力扣(LeetCode)
40、优先级队列(堆)的模拟实现
public class PriorityQueue {
public int[] elem;
public int usedSize;
public PriorityQueue() {
}
/**
* 建堆的时间复杂度:
*
* @param array
*/
public void createHeap(int[] array) {
}
/**
*
* @param root 是每棵子树的根节点的下标
* @param len 是每棵子树调整结束的结束条件
* 向下调整的时间复杂度:O(logn)
*/
private void shiftDown(int root,int len) {
}
/**
* 入队:仍然要保持是大根堆
* @param val
*/
public void push(int val) {
}
private void shiftUp(int child) {
}
public boolean isFull() {
}
/**
* 出队【删除】:每次删除的都是优先级高的元素
* 仍然要保持是大根堆
*/
public void pollHeap() {
}
public boolean isEmpty() {
}
/**
* 获取堆顶元素
* @return
*/
public int peekHeap() {
}
}
public class PriorityQueue {
public int[] elem;
public int usedSize;
public PriorityQueue() {
this.elem = new int[10];
}
/**
* 建堆的时间复杂度:
*
* @param array
*/
public void createHeap(int[] array) {
//这一步不算是必须的。这里只是我们准备数据,不算做我的建堆时间复杂度当中
for (int i = 0; i < array.length; i++) {
elem[i] = array[i];
usedSize++;
}
for (int p = (usedSize-1-1)/2; p >= 0 ; p--) {
shiftDown(p,usedSize);
}
}
/**
*
* @param root 是每棵子树的根节点的下标
* @param len 是每棵子树调整结束的结束条件
* 向下调整的时间复杂度:O(logn)
*/
private void shiftDown(int root,int len) {
int parent = root;
int child = 2*parent+1;
//进入这个循环,说明一定至少有一个孩子
while (child < len) {
//如果有孩子,找到左右孩子的最大值
if(child+1 < len && elem[child] < elem[child+1]) {
child++;
}
//child下标一定保存的是左右孩子最大值的下标
//接下来,孩子的最大值和根节点去比较大小
if(elem[child] > elem[parent]) {
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
parent = child;//开始更新下标,继续看下面的子树是不是大根堆
child = 2*parent+1;
}else {
break;//此时说明已经是大根堆,不需要进行再次调整了
}
}
}
/**
* 入队:仍然要保持是大根堆
* @param val
*/
public void push(int val) {
if(isFull()) {
elem = Arrays.copyOf(elem,2*elem.length);
}
//1、放到最后的位置
elem[usedSize] = val;
//2、进行向上调整
shiftUp(usedSize);
//3、有效数据+1
usedSize++;
}
private void shiftUp(int child) {
int parent = (child-1) / 2;
while (child > 0) {
if(elem[child] > elem[parent]) {
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
child = parent;
parent = (child-1)/2;
}else {
break;
}
}
}
public boolean isFull() {
return usedSize == elem.length;
}
/**
* 出队【删除】:每次删除的都是优先级高的元素
* 仍然要保持是大根堆
*/
public void pollHeap() {
if(isEmpty()) {
System.out.println("优先级队列为空!");
return;
}
int tmp = elem[0];
elem[0] = elem[usedSize-1];
elem[usedSize-1] = tmp;
usedSize--;//9
shiftDown(0,usedSize);
}
public boolean isEmpty() {
return usedSize == 0;
}
/**
* 获取堆顶元素
* @return
*/
public int peekHeap() {
if(isEmpty()) {
System.out.println("优先级队列为空!");
return -1;
}
return elem[0];
}
}
41、插入排序和希尔排序
//=====================================================
// 插入排序
public static void insertSort(int[] array){
// 外层循环目的:取到数组中的每个元素
for(int i = 1; i < array.length; ++i){
// 将该元素插入到序列中
// array数组中:[0, i)的元素已经排好了
// i位置的数据是本次要插入的数据---[0,i)
int key = array[i];
int end = i-1;
while(end >= 0 && key < array[end]){
array[end+1] = array[end];
end--;
}
array[end+1] = key;
}
}
//===========================================================
// 希尔排序
public static void shellSort(int[] array){
int gap = array.length;
while(gap > 1){
gap = gap/3+1;
for(int i = gap; i < array.length; ++i){
// 将该元素插入到序列中
// array数组中:[0, i)的元素已经排好了
// i位置的数据是本次要插入的数据---[0,i)
int key = array[i];
int end = i-gap;
while(end >= 0 && key < array[end]){
array[end+gap] = array[end];
end -= gap;
}
array[end+gap] = key;
}
}
}
42、选择排序和堆排序
//============================================================
// 选择排序
// 时间复杂度:O(N^2)
// 空间复杂度:O(1)
// 稳定性:不稳定
public static void selectSort(int[] array){
int size = array.length;
for(int i = 0; i < size-1; ++i){ // 控制具体选择的趟数// 具体选择的方式
int pos = 0;
// 找最大元素的位置
for(int j = 0; j < size-i; ++j){
if(array[j] > array[pos]){
pos = j;
}
}
// 用pos标记的最大元素与区间最后一个位置上的元素交换
if(pos != size-1-i){
swap(array, pos, size-i-1);
}
}
}
public static void selectSortOP(int[] array){
int begin = 0;
int end = array.length-1;
while(begin < end){
// 在[begin, end]区间中找最大和最小元素的位置
// 最大元素的位置使用maxPos标记
// 最小元素的位置使用minPos标记
int minPos = begin;
int maxPos = begin;
int index = begin+1;
while(index <= end){
if(array[index] > array[maxPos]){
maxPos = index;
}
if(array[index] < array[minPos]){
minPos = index;
}
index++;
}
// 在[begin, end]区间中,已经找到了最小与最小元素的位置
if(maxPos != end){
swap(array, maxPos, end);
}
if(minPos == end){
minPos = maxPos;
}
if(minPos != begin){
swap(array, minPos, begin);
}
begin++;
end--;
}
}
//================================================================
// 堆排序
public static void shiftDown(int[] array, int size, int parent){
// child标记parent的左孩子---parent可能有左没有右
int child = parent*2 + 1;
while(child < size){ // 如果循环条件成立:parent的左孩子一定是成立的
// 右孩子存在的情况下,找左右孩子中最大的孩子
if(child+1 < size && array[child+1] > array[child]){
child += 1;
}
// 检测parent是否满足堆的特性
if(array[parent] < array[child]){
swap(array, parent, child);
parent = child;
child = parent*2+1;
}else{
return;
}
}
}
// 时间复杂度:O(NlogN)
// 空间复杂度:O(1)
// 稳定性:不稳定
// 应用场景:需要一个序列中前k个最大||最小 ---- 可能会和其他排序复合起来提高排序的效率
public static void heapSort(int[] array){
// 1. 建堆
// a. 找倒数第一个非叶子节点
int size = array.length;
int lastLeaf = ((size-2)>>>1);
for(int root = lastLeaf; root >= 0; root--){
shiftDown(array, size, root);
}
// 2. 利用堆删除的思想排序
int end = size-1;
while(end > 0){
// 用堆顶元素与堆中最后一个元素交换
swap(array, 0, end);
// 将堆中有效元素个数减少一个
// 将堆顶元素向下调整
shiftDown(array, end,0); // logN
end--;
}
}
43、冒泡排序和快速排序
//================================================================
// 冒泡排序
// 时间复杂度:O(N^2)
// 空间复杂度:O(1)
// 稳定性:稳定
// 应用场景:
public static void bubbleSort(int[] array){
int size = array.length;
for(int i = 0; i < size; ++i){ // 控制冒泡的趟数
boolean isChange = false;
// 具体冒泡的方式:用相邻的两个元素进行交换// 如果不满足条件则交换for(int j = 1; j < size - i; j++){
if(array[j-1] > array[j]){
swap(array, j-1, j);
isChange = true;
}
}
if(!isChange){
return;
}
}
}
//============================================================
// 快速排序
// 三数取中法:
public static int getIndexOfMiddle(int[] array, int left, int right){
// left
// mid: left + ((right-left)>>1)
// right-1
int mid = left + ((right-left)>>1);
if(array[left] < array[right-1]){
if(array[mid] < array[left]){
return left;
}else if(array[mid] > array[right-1]){
return right-1;
}else{
return mid;
}
}else{
if(array[mid] > array[left]){
return left;
}else if(array[mid] < array[right-1]){
return right-1;
}else{
return mid;
}
}
}
// Hoare法时间复杂度:O(N)
public static int partition1(int[] array, int left, int right){
int index = getIndexOfMiddle(array, left, right);
if(index != right-1){
swap(array, index, right-1);
}
int key = array[right-1];
int begin = left;
int end = right-1;
while(begin < end){
// 让begin从前往后找比key大的元素,找到之后停下来
while(begin < end && array[begin] <= key){
begin++;
}
// 让end从后往前找比key小的元素,找到之后停下来
while(begin < end && array[end] >= key){
end--;
}
if(begin != end){
swap(array, begin, end);
}
}
if(begin != right-1){
swap(array, begin, right-1);
}
return begin;
}
// 挖坑法----时间复杂度:O(N)
public static int partition2(int[] array, int left, int right){
int index = getIndexOfMiddle(array, left, right);
if(index != right-1){
swap(array, index, right-1);
}
int key = array[right-1];
int begin = left;
int end = right-1;
while(begin < end){
// 让begin从前往后找比基准值大的元素
while(begin < end && array[begin] <= key){
begin++;
}
if(begin < end){
// begin位置找到了一个比key大的元素,填end位置的坑
array[end] = array[begin];
// begin位置就形成一个新的坑位
}
// 让end从后往前找比基准值小的元素
while(begin < end && array[end] >= key){
end--;
}
if(begin < end){
// end位置找到了一个比key小的元素,填begin位置的坑
array[begin] = array[end];
// end位置就形成了一个新的坑位
}
}
// begin和end位置是最后的一个坑位
// 这个坑位使用基准值填充
array[begin] = key;
return begin;
}
public static int partition(int[] array, int left, int right){
int cur = left;
int prev = cur-1;
int index = getIndexOfMiddle(array, left, right);
if(index != right-1){
swap(array, index, right-1);
}
int key = array[right-1];
// 让cur从前往后找比key小的元素
while(cur < right){
if(array[cur] < key && ++prev != cur){
swap(array, cur, prev);
}
++cur;
}
// 将基准值的位置放置好
if(++prev != right -1){
swap(array, prev, right-1);
}
return prev;
}
// 时间复杂度:O(NlogN)
// 空间复杂度:O(logN)
// 稳定性:不稳定
// 应用场景:数据越随机越好----越乱越好
public static void insertSortQuick(int[] array, int left, int right){
// 外层循环目的:取到数组中的每个元素
for(int i = left+1; i < right; ++i){
// 将该元素插入到序列中
// array数组中:[0, i)的元素已经排好了
// i位置的数据是本次要插入的数据---[0,i)
int key = array[i];
int end = i-1;
while(end >= 0 && key < array[end]){
array[end+1] = array[end];
end--;
}
array[end+1] = key;
}
}
public static void quickSort(int[] array, int left, int right){
if(right - left < 47) {
insertSortQuick(array, left, right);
}else{
// 假设升序
// 找一个基准值将[left, right)区间分割成两个部分
int div = partition(array, left, right);
// 左侧部分比基准值小
// [left, div)
quickSort(array, left, div);
// 右侧部分比基准值大
// [div+1, right)
quickSort(array, div+1, right);
}
}
public static void quickSort(int[] array){
Stack<Integer> s = new Stack<>();
s.push(array.length);
s.push(0);
while(!s.empty()){
int left = s.pop();
int right = s.pop();
if(right - left < 47){
insertSortQuick(array, left, right);
}else{
int div = partition2(array, left, right);
// 先压入基准值右侧部分
// [div+1, right);
s.push(right);
s.push(div+1);
// 后压入基准值左侧部分
// [left, div)
s.push(div);
s.push(left);
}
}
}
44、归并排序和计数排序
//==========================================================
// 归并排序
public static void mergeDate(int[] array, int left, int mid, int right, int[] temp){
int begin1 = left, end1 = mid;
int begin2 = mid, end2 = right;
int index = left;
while(begin1 < end1 && begin2 < end2){
if(array[begin1] <= array[begin2]){
temp[index++] = array[begin1++];
}else{
temp[index++] = array[begin2++];
}
}
while(begin1 < end1){
temp[index++] = array[begin1++];
}
while(begin2 < end2){
temp[index++] = array[begin2++];
}
}
// 时间复杂度:O(NlogN)
// 空间复杂度:O(N)
// 稳定性:稳定
// 应用场景:外部排序
private static void mergeSort(int[] array, int left, int right, int[] temp){
if(right - left > 1){
// 先对[left, right)区间中的元素进行均分
int mid = left + ((right - left)>>1);
// [left, mid)
mergeSort(array, left, mid, temp);
// [mid, right)
mergeSort(array, mid, right, temp);
mergeDate(array, left, mid, right, temp);
System.arraycopy(temp, left, array, left, right-left);
}
}
public static void mergeSort(int[] array){
int[] temp = new int[array.length];
mergeSort(array, 0, array.length, temp);
}
public static void mergeSortNor(int[] array){
int size = array.length;
int[] temp = new int[size];
int gap = 1;
while(gap < size){
for(int i = 0; i < size; i+= 2*gap){
int left = i;
int mid = left + gap;
int right = mid+gap;
if(mid > size){
mid = size;
}
if(right > size){
right = size;
}
mergeDate(array, left, mid, right, temp);
}
System.arraycopy(temp, 0, array, 0, size);
gap <<= 1;
}
}
// 计数排序
//==================================================================
// 时间复杂度:O(N)---N表示数组中元素个数
// 空间复杂度:O(M)---M表示范围的大小
// 稳定性:稳定
// 应用场景:数据密集集中在某个范围中
public static void countSort(int[] array){
// 找数据范围
int maxValue = array[0];
int minValue = array[0];
for(int i = 0; i < array.length; ++i){
if(array[i] > maxValue){
maxValue = array[i];
}
if(array[i] < minValue){
minValue = array[i];
}
}
// 计算计数空间的大小
int range = maxValue - minValue + 1;
int[] count = new int[range];
// 1. 统计array中每个数据出现的次数
for(int i = 0; i < array.length; ++i){
count[array[i] - minValue]++;
}
// 2. 根据统计结果回收
int index = 0;
for(int i = 0; i < range; ++i){
while(count[i] > 0){
array[index++] = i + minValue;
count[i]--;
}
}
}
46、实现二叉搜索树代码
public class BinarySearchTree {
static class TreeNode {
public int key;
public TreeNode left;
public TreeNode right;
TreeNode(int key) {
this.key = key;
}
}
public TreeNode root;
/**
*
* @param key
*/
public boolean insert(int key) {
if(root == null) {
root = new TreeNode(key);
return true;
}
TreeNode cur = root;
TreeNode parent = null;
while (cur != null) {
if(cur.key < key) {
parent = cur;
cur = cur.right;
}else if(cur.key > key) {
parent = cur;
cur = cur.left;
}else {
return false;
}
}
TreeNode node = new TreeNode(key);
//当代码执行到这里之后。cur == null
if(parent.key < key) {
parent.right = node;
}else {
parent.left = node;
}
return true;
}
public TreeNode search(int key) {
TreeNode cur = root;
while (cur != null) {
if(cur.key == key) {
return cur;
}else if(cur.key < key) {
cur = cur.right;
}else {
cur = cur.left;
}
}
return null;
}
public boolean remove(int key) {
TreeNode cur = root;
TreeNode parent = null;
while (cur != null) {
if(cur.key < key) {
parent = cur;
cur = cur.right;
}else if(cur.key == key) {
//这里开始删除!
removeNode(parent,cur);
return true;
}else {
parent = cur;
cur = cur.left;
}
}
return false;
}
private void removeNode(TreeNode parent,TreeNode cur) {
if(cur.left == null) {
if(cur == root) {
root = cur.right;
}else if(cur == parent.left) {
parent.left = cur.right;
}else {
parent.right = cur.right;
}
}else if(cur.right == null) {
if(cur == root) {
root = cur.left;
}else if(cur == parent.left) {
parent.left = cur.left;
}else {
parent.right = cur.left;
}
}else {
TreeNode targetParent = cur;
TreeNode target = cur.right;
while (target.left != null) {
targetParent = target;
target = target.left;
}
cur.key = target.key;
if(target == targetParent.left) {
targetParent.left = target.right;
}else {
targetParent.right = target.right;
}
}
}
//中序遍历即可
public void inorder(TreeNode root) {
if(root == null) return;
inorder(root.left);
System.out.print(root.key+" ");
inorder(root.right);
}
}
47、二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com)