数据结构与算法拾遗五
比较器
比较器常规用法
主要记住返回1则是第二个参数在前
返回-1则是第一个参数在前
public static class Student {
public String name;
public int id;
public int age;
public Student(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
}
// 谁id小,谁放前!
public static class IdComparator implements Comparator<Student> {
// 如果返回负数,认为第一个参数应该排在前面
// 如果返回正数,认为第二个参数应该排在前面
// 如果返回0,认为谁放前面无所谓
@Override
public int compare(Student o1, Student o2) {
if (o1.id < o2.id) {
return -1;
} else if (o2.id < o1.id) {
return 1;
} else {
return 0;
}
}
}
// 谁age大,谁放前!
public static class AgeComparator implements Comparator<Student> {
// 如果返回负数,认为第一个参数应该排在前面
// 如果返回正数,认为第二个参数应该排在前面
// 如果返回0,认为谁放前面无所谓
@Override
public int compare(Student o1, Student o2) {
if (o1.age < o2.age) {
return 1;
} else if (o2.age < o1.age) {
return -1;
} else {
return 0;
}
}
}
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void printStudents(Student[] students) {
for (int i = 0; i < students.length; i++) {
System.out.println(students[i].name + ", " + students[i].id + ", " + students[i].age);
}
}
public static void main(String[] args) {
int[] arr = { 8, 1, 4, 1, 6, 8, 4, 1, 5, 8, 2, 3, 0 };
printArray(arr);
Arrays.sort(arr);
printArray(arr);
Student s1 = new Student("张三", 5, 27);
Student s2 = new Student("李四", 1, 17);
Student s3 = new Student("王五", 4, 29);
Student s4 = new Student("赵六", 3, 9);
Student s5 = new Student("左七", 2, 34);
Student[] students = { s1, s2, s3, s4, s5 };
printStudents(students);
System.out.println("=======");
Arrays.sort(students, new IdComparator());
printStudents(students);
System.out.println("=======");
ArrayList<Student> arrList = new ArrayList<>();
arrList.add(s1);
arrList.add(s2);
arrList.add(s3);
arrList.add(s4);
arrList.add(s5);
for (Student s : arrList) {
System.out.println(s.name + ", " + s.id + ", " + s.age);
}
System.out.println("=======");
arrList.sort(new AgeComparator());
for (Student s : arrList) {
System.out.println(s.name + ", " + s.id + ", " + s.age);
}
}
比较器对于优先级队列的使用:
优先队列默认为小根堆,从小到大排序
队顶元素为最小值
// 负,第一个参数在前
// 正,第二个参数在前
// 0, 谁放前都行
@Override
public int compare(Integer o1, Integer o2) {
if (o1 < o2) {
return 1;
} else if (o1 > o2) {
return -1;
} else {
return 0;
}
}
}
public static class Student {
public String name;
public int id;
public int age;
public Student(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
}
// 谁id大,谁放前!
public static class IdComparator implements Comparator<Student> {
// 如果返回负数,认为第一个参数应该排在前面
// 如果返回正数,认为第二个参数应该排在前面
// 如果返回0,认为谁放前面无所谓
@Override
public int compare(Student o1, Student o2) {
if (o1.id < o2.id) {
return 1;
} else if (o2.id < o1.id) {
return -1;
} else {
return 0;
}
}
}
public static void main(String[] args) {
String str1 = "abc";
String str2 = "b";
//String之间的比较为字典序之间的比较 如下返回-1
System.out.println(str1.compareTo(str2));
PriorityQueue<Student> heap = new PriorityQueue<>(new IdComparator());
Student s1 = new Student("张三", 5, 27);
Student s2 = new Student("李四", 1, 17);
Student s3 = new Student("王五", 4, 29);
Student s4 = new Student("赵六", 3, 9);
Student s5 = new Student("左七", 2, 34);
heap.add(s1);
heap.add(s2);
heap.add(s3);
heap.add(s4);
heap.add(s5);
System.out.println("=========");
while (!heap.isEmpty()) {
Student s = heap.poll();
System.out.println(s.name + ", " + s.id + ", " + s.age);
}
}
当然也可以改成如下lambda的形式
PriorityQueue<Student> heap = new PriorityQueue<>((Student o1, Student o2) ->o1.id > o2.id ? -1 : 1);
PriorityQueue<Student> heap = new PriorityQueue<>((Student o1, Student o2) -> o2.id - o1.id);
合并k个升序链表
题目链接:https://leetcode.cn/problems/merge-k-sorted-lists/
思路:将所有链表的头节点入优先队列,然后取出这个队列的头部元素(最小的),然后再把这个头部元素指向的下一个节点放入优先队列中。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
//首先写一个比较器
public static class ListNodeComparator implements Comparator<ListNode> {
@Override
public int compare(ListNode o1, ListNode o2) {
return o1.val - o2.val;
}
}
public ListNode mergeKLists(ListNode[] lists) {
if (lists == null) {
return null;
}
PriorityQueue<ListNode> heap = new PriorityQueue<>(new ListNodeComparator() );
for (int i = 0; i < lists.length; i++) {
if (lists[i] != null) {
heap.add(lists[i]);
}
}
if(heap.isEmpty()) {
return null;
}
ListNode head = heap.poll();
ListNode pre = head;
if (pre.next != null) {
heap.add(pre.next);
}
while (!heap.isEmpty()) {
ListNode cur = heap.poll();
pre.next = cur;
pre = cur;
if (cur.next != null) {
heap.add(cur.next);
}
}
return head;
}
}
如上最多一次有M个根节点进入小根堆 然后,总共有N个节点
对于小根堆的操作为logM
然后对于节点是有N个,要出去进来N次那么时间复杂度则为N*logM
二叉树
二叉树前中后序遍历:
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int v) {
value = v;
}
}
public static void f(Node head) {
if (head == null) {
return;
}
// 1
f(head.left);
// 2
f(head.right);
// 3
}
// 先序打印所有节点
public static void pre(Node head) {
if (head == null) {
return;
}
System.out.println(head.value);
pre(head.left);
pre(head.right);
}
public static void in(Node head) {
if (head == null) {
return;
}
in(head.left);
System.out.println(head.value);
in(head.right);
}
public static void pos(Node head) {
if (head == null) {
return;
}
pos(head.left);
pos(head.right);
System.out.println(head.value);
}
public static void main(String[] args) {
Node head = new Node(1);
head.left = new Node(2);
head.right = new Node(3);
head.left.left = new Node(4);
head.left.right = new Node(5);
head.right.left = new Node(6);
head.right.right = new Node(7);
pre(head);
System.out.println("========");
in(head);
System.out.println("========");
pos(head);
System.out.println("========");
}
如上代码的递归序为:
1,2,4,4,4,2,5,5,5,2,1,3,6,6,6,3,7,7,7,3,1
前中后序遍历则是
前序遍历:第一次来到一个数
中序遍历:第二次来到一个数
后序遍历:第三次来到一个数
判断两棵树是否相等
直接通过先序遍历判断各节点即可:
题目链接: https://leetcode.cn/problems/same-tree/submissions/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null ^ q == null) {
return false;
}
if (p == null && q == null) {
return true;
}
return p.val == q.val && isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
}
判断一棵树是否为镜面树
一棵树分两个分支,左边分支的左节点要和右边分支的右节点一样,
左边分支的右节点要和右边分支的左节点一样。
如下传入将根节点当成一个节点和他的对称节点,然后分别往这棵树的左右两个分支递归,
先找左边分支的左边节点是否和右边分支的右节点相等,再找左边分支的右边节点是否和左边分支的左节点相等。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
return isMirror(root, root);
}
public boolean isMirror(TreeNode p, TreeNode q) {
if (p == null ^ q == null) {
return false;
}
if (p == null && q == null) {
return true;
}
return p.val == q.val && isMirror(p.left, q.right) && isMirror(p.right, q.left);
}
}