思路:数组是有序的,从小到大排序,利用这个特性,可以二分查找法,查找的数与中间的数比大小,如果中间的数大于查找的数,说明查找的数在左边,R = mid - 1。如果中间的数小于查找的数,说明查找的数在右边,L = mid + 1。
public class Code01_BSExist {
// 有序数组
public static boolean find(int[] arr, int num) {
if (arr == null || arr.length == 0) {
return false;
}
int L = 0;
int R = arr.length - 1;
while (L <= R) {
int middle = (L + R) / 2;
if (arr[middle] == num) {
return true;
} else if (arr[middle] < num) {
L = middle + 1;
} else {
R = middle - 1;
}
}
return false;
}
// for test
public static boolean test(int[] arr, int num) {
for (int cur : arr) {
if (cur == num) {
return true;
}
}
return false;
}
// for test
public static int[] generateRandomArray(int maxSize, int maxValue) {
int len = (int) (Math.random() * maxSize) + 1;
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
int ans = (int) (Math.random() * maxValue);
arr[i] = ans;
}
return arr;
}
public static void main(String[] args) {
int testTimes = 1000000;
int maxSize = 100;
int maxValue = 1000;
boolean succeed = true;
for (int i = 0; i < testTimes; i++) {
int[] arr = generateRandomArray(maxSize, maxValue);
Arrays.sort(arr);
int num = (int) (Math.random() * maxValue);
if (find(arr, num) != test(arr, num)) {
System.out.println("出错了!");
succeed = false;
break;
}
}
System.out.println(succeed ? "Nice!" : "Fucking fucked!");
}
}
思路:利用二分查找法,如果中间数大于查找的数,说明查找的数在左边,则 R = mid - 1,此时需要用变量记住当前索引位置,如果中间数小于查找的数,说明查找的数在右边,则 L = mid + 1,此时不需要用变量记录当前索引,因为题目要求>=num最左的位置,而从中间的数左半部分都小于num,就不用考虑了。
public class Code02_BSNearLeft {
// arr有序的,>=num 最左
public static int mostLeftNoLessNumIndex(int[] arr, int num) {
if (arr == null || arr.length == 0) {
return -1;
}
int L = 0;
int R = arr.length - 1;
int ans = -1;
while (L <= R) {
int middle = (L + R) / 2;
if (arr[middle] >= num) {
R = middle - 1;
ans = middle;
} else {
L = middle + 1;
}
}
return ans;
}
// for test
public static int test(int[] arr, int value) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] >= value) {
return i;
}
}
return -1;
}
// for test
public static int[] generateRandomArray(int maxSize, int maxValue) {
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
}
return arr;
}
// for test
public static void printArray(int[] arr) {
if (arr == null) {
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int testTime = 500000;
int maxSize = 10;
int maxValue = 100;
boolean succeed = true;
for (int i = 0; i < testTime; i++) {
int[] arr = generateRandomArray(maxSize, maxValue);
Arrays.sort(arr);
int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
if (test(arr, value) != mostLeftNoLessNumIndex(arr, value)) {
printArray(arr);
System.out.println(value);
System.out.println(test(arr, value));
System.out.println(mostLeftNoLessNumIndex(arr, value));
succeed = false;
break;
}
}
System.out.println(succeed ? "Nice!" : "Fucking fucked!");
}
}
思路:利用二分查找法,如果中间的数小于查找的数,则 L = mid + 1,记录当前索引的位置。如果中间的数大于查找的数,则 R = mid - 1,此时不需要记录当前索引的位置,因为题目要求<=num最有的位置,中间的数及右边部分都大于查找的数,直接排除。
public class Code03_BSNearRight {
// 在arr上,找满足<=value的最右位置
public static int nearestIndex(int[] arr, int value) {
int L = 0;
int R = arr.length - 1;
int index = -1; // 记录最右的对号
while (L <= R) {
int mid = L + ((R - L) >> 1);
if (arr[mid] <= value) {
index = mid;
L = mid + 1;
} else {
R = mid - 1;
}
}
return index;
}
// for test
public static int test(int[] arr, int value) {
for (int i = arr.length - 1; i >= 0; i--) {
if (arr[i] <= value) {
return i;
}
}
return -1;
}
// for test
public static int[] generateRandomArray(int maxSize, int maxValue) {
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
}
return arr;
}
// for test
public static void printArray(int[] arr) {
if (arr == null) {
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int testTime = 500000;
int maxSize = 10;
int maxValue = 100;
boolean succeed = true;
for (int i = 0; i < testTime; i++) {
int[] arr = generateRandomArray(maxSize, maxValue);
Arrays.sort(arr);
int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
if (test(arr, value) != nearestIndex(arr, value)) {
printArray(arr);
System.out.println(value);
System.out.println(test(arr, value));
System.out.println(nearestIndex(arr, value));
succeed = false;
break;
}
}
System.out.println(succeed ? "Nice!" : "Fucking fucked!");
}
}
局部最小值问题推出二分查找的前提,数组不一定必须是有序的!!!
思路:如果数组只有一个元素,则返回,如果数组有2个元素,则比较2个元素的大小,进行返回索引位。抛出这两种可能外,则L...R 肯定有最小值,局部最小值问题,它不是说在数组里找找最小值,而是局部只要最小就行,只要当前这个数比左边小,且比右边小,就满足局部最小原则。
public class Code04_BSAwesome {
// arr 整体无序
// arr 相邻的数不相等!
public static int oneMinIndex(int[] arr) {
if (arr == null || arr.length == 0) {
return -1;
}
int N = arr.length;
if (N == 1) {
return 0;
}
if (arr[0] < arr[1]) {
return 0;
}
if (arr[N - 1] < arr[N - 2]) {
return N - 1;
}
int L = 0;
int R = N - 1;
// L...R 肯定有局部最小
while (L < R - 1) {
int mid = (L + R) / 2;
if (arr[mid] < arr[mid - 1] && arr[mid] < arr[mid + 1]) {
return mid;
} else {
if (arr[mid] > arr[mid - 1]) {
R = mid - 1;
} else {
L = mid + 1;
}
}
}
return arr[L] < arr[R] ? L : R;
}
// 生成随机数组,且相邻数不相等
public static int[] randomArray(int maxLen, int maxValue) {
int len = (int) (Math.random() * maxLen);// [0,maxLen - 1]
int[] arr = new int[len];
if (len > 0) {
arr[0] = (int) (Math.random() * maxValue);
for (int i = 1; i < len; i++) {
do {
arr[i] = (int) (Math.random() * maxValue);
} while (arr[i] == arr[i - 1]);
}
}
return arr;
}
// 也用于测试
public static boolean check(int[] arr, int minIndex) {
if (arr.length == 0) {
return minIndex == -1;
}
int left = minIndex - 1;
int right = minIndex + 1;
boolean leftBigger = left >= 0 ? arr[left] > arr[minIndex] : true;
boolean rightBigger = right < arr.length ? arr[right] > arr[minIndex] : true;
return leftBigger && rightBigger;
}
public static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
public static void main(String[] args) {
int maxLen = 100;
int maxValue = 200;
int testTime = 1000000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int[] arr = randomArray(maxLen, maxValue);
int ans = oneMinIndex(arr);
if (!check(arr, ans)) {
printArray(arr);
System.out.println(ans);
break;
}
}
System.out.println("测试结束");
}
}
单链表:值,一条next指针
双链表:值,一条pre指针,一条next指针
玩出无尽的花样!
经典题目:
给定一个单链表的头head,完成链表的逆序调整
public class Code01_ReverseList {
public static class Node {
public int value;
public Node next;
public Node(int data) {
this.value = data;
}
}
// 单链表反转
public static Node reverseLinkedList(Node head) {
Node pre = null;
Node next = null;
while (head != null) {
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
public static void main(String[] args) {
// 单链表反转测试
Node node1 = new Node(1);
node1.next = new Node(2);
node1.next.next = new Node(3);
Node head = reverseLinkedList(node1);
while (head != null) {
System.out.print(head.value + " ");
head = head.next;
}
System.out.println();
}
}
给定一个双链表的头head,完成链表的逆序调整
public class Code01_ReverseList {
public static class DoubleNode {
public int value;
public DoubleNode last;
public DoubleNode next;
public DoubleNode(int data) {
this.value = data;
}
@Override
public String toString() {
return value + "";
}
}
// 双链表反转
public static DoubleNode reverseDoubleList(DoubleNode head) {
DoubleNode pre = null;
DoubleNode next = null;
while (head != null) {
next = head.next;
head.next = pre;
head.last = next;
pre = head;
head = next;
}
return pre;
}
public static void main(String[] args) {
// 双链表反转测试
DoubleNode doubleNode1 = new DoubleNode(1);
doubleNode1.next = new DoubleNode(2);
doubleNode1.next.next = new DoubleNode(3);
doubleNode1.next.next.last = doubleNode1.next;
doubleNode1.next.last = doubleNode1;
DoubleNode doubleNodeHead = reverseDoubleList(doubleNode1);
while (doubleNodeHead != null) {
System.out.println(doubleNodeHead.next + "<-" + doubleNodeHead + "->" + doubleNodeHead.last);
doubleNodeHead = doubleNodeHead.next;
}
System.out.println();
}
}
public class Code02_LinkedListToQueueAndStack {
public static class Node<V> {
public V value;
public Node<V> next;
public Node(V data) {
this.value = data;
}
}
public static class MyQueue<V> {
private Node<V> head;
private Node<V> tail;
private int size;
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
public void offer(V data) {
Node<V> cur = new Node<>(data);
if (tail == null) {
head = tail = cur;
} else {
tail.next = cur;
tail = cur;
}
size++;
}
public V poll() {
V ans = null;
if (head != null) {
ans = head.value;
head = head.next;
size--;
}
if (head == null) {
tail = null;
}
return ans;
}
public V peek() {
V ans = null;
if (head != null) {
ans = head.value;
}
return ans;
}
}
public static void main(String[] args) {
MyQueue queue = new MyQueue();
queue.offer(1);
queue.offer(3);
queue.offer(2);
while (!queue.isEmpty()) {
System.out.print(queue.poll() + " ");
}
System.out.println();
}
}
public class Code02_LinkedListToQueueAndStack {
public static class Node<V> {
public V value;
public Node<V> next;
public Node(V data) {
this.value = data;
}
}
public static class MyStack<V> {
private Node<V> head;
private int size;
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
public void push(V data) {
Node<V> cur = new Node<>(data);
if (head != null) {
cur.next = head;
}
head = cur;
size++;
}
public V pop() {
V ans = null;
if (head != null) {
ans = head.value;
head = head.next;
size--;
}
return ans;
}
public V peek() {
return head != null ? head.value : null;
}
}
public static void main(String[] args) {
MyStack stack = new MyStack();
stack.push(6);
stack.push(1);
stack.push(3);
while (!stack.isEmpty()) {
System.out.print(stack.pop() + " ");
}
System.out.println();
}
}
用双链表结构实现双端队列
public class Code03_DoubleLinkedListDeque {
public static class Node<V> {
public V value;
public Node<V> next;
public Node<V> last;
public Node(V data) {
this.value = data;
}
}
public static class MyDeque<V> {
private Node<V> head;
private Node<V> tail;
private int size;
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
// 头部添加
public void pushHead(V data) {
Node<V> cur = new Node<>(data);
if (head == null) {
head = tail = cur;
} else {
cur.next = head;
head.last = cur;
head = cur;
}
size++;
}
// 尾部添加
public void pushTail(V data) {
Node<V> cur = new Node<>(data);
if (head == null) {
head = tail = cur;
} else {
tail.next = cur;
cur.last = tail;
tail = cur;
}
size++;
}
// 头部取出
public V pollHead() {
V ans = null;
if (head == null) {
return ans;
}
size--;
ans = head.value;
if (head == tail) {
head = tail = null;
} else {
head = head.next;
head.last = null;
}
return ans;
}
// 尾部取出
public V pollTail() {
V ans = null;
if (head == null) {
return ans;
}
size--;
ans = tail.value;
if (head == tail) {
head = tail = null;
} else {
tail = tail.last;
tail.next = null;
}
return ans;
}
// 查看头部
public V peekHead() {
return head != null ? head.value : null;
}
// 查看尾部
public V peekTail() {
return tail != null ? tail.value : null;
}
}
}
给定一个单链表的头节点head,和一个正数k,实现k个节点的小组内部逆序,如果最后一组不够k个就不调整。
例子:
调整前:1->2->3->4->5->6->7->8 k = 3
调整后:3->2->1->6->5->4->7->8
public class Code04_ReverseNodesInKGroup {
public static class ListNode {
public int val;
public ListNode next;
public ListNode(int data) {
this.val = data;
}
}
public static ListNode reverseKGroup(ListNode head, int k) {
ListNode start = head;
ListNode end = getKGroupEnd(start, k);
if (end == null) {
return head;
}
head = end;
reverse(start, end);
ListNode lastEnd = start;
while (lastEnd.next != null) {
start = lastEnd.next;
end = getKGroupEnd(start, k);
if (end == null) {
return head;
}
reverse(start, end);
lastEnd.next = end;
lastEnd = start;
}
return head;
}
// 从start开始,返回k位置上的节点
public static ListNode getKGroupEnd(ListNode start, int k) {
while (--k != 0 && start != null) {
start = start.next;
}
return start;
}
// 从start到end,反转链表
public static void reverse(ListNode start, ListNode end) {
end = end.next;
ListNode cur = start;
ListNode pre = null;
ListNode next = null;
while (cur != end) {
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
start.next = end;
}
public static void print(ListNode head) {
while (head != null) {
System.out.print(head.val + "->");
head = head.next;
}
System.out.println();
}
public static void main(String[] args) {
// 1->2->3->4->5->6->7->8 k = 3
ListNode node1 = new ListNode(1);
ListNode node2 = new ListNode(2);
ListNode node3 = new ListNode(3);
ListNode node4 = new ListNode(4);
ListNode node5 = new ListNode(5);
ListNode node6 = new ListNode(6);
ListNode node7 = new ListNode(7);
ListNode node8 = new ListNode(8);
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = node6;
node6.next = node7;
node7.next = node8;
print(node1);
// 开始
ListNode head = reverseKGroup(node1, 3);
print(head);
}
}
给定两个链表的头节点head1和head2,认为从左到右是某个数字从低位到高位,返回相加之后的链表
例子 4->3->6 2->5->3
返回 6->8->9
解释 634 + 352 = 986
public class Code05_AddTwoNumbers {
public static class ListNode {
private int val;
private ListNode next;
public ListNode(int data) {
this.val = data;
}
}
public static ListNode addTwoNumbers(ListNode head1, ListNode head2) {
int len1 = listLength(head1);
int len2 = listLength(head2);
ListNode l = len1 >= len2 ? head1 : head2;
ListNode s = l == head1 ? head2 : head1;
ListNode curL = l;
ListNode curS = s;
ListNode last = curL;
int carry = 0;
int curNum = 0;
while (curS != null) {
curNum = curL.val + curS.val + carry;
curL.val = (curNum % 10);
carry = curNum / 10;
last = curL;
curL = curL.next;
curS = curS.next;
}
while (curL != null) {
curNum = curL.val + carry;
curL.val = (curNum % 10);
carry = curNum / 10;
last = curL;
curL = curL.next;
}
if (carry != 0) {
last.next = new ListNode(1);
}
return l;
}
// 求链表长度
public static int listLength(ListNode head) {
int len = 0;
while (head != null) {
len++;
head = head.next;
}
return len;
}
}
给定两个有序链表的头节点head1和head2,返回合并之后的大链表,要求依然有序。
例子 1->3->3->5->7 2->2->3->3->7
返回 1->2->2->3->3->3->3->5->7->7
public class Code06_MergeTwoSortedLinkedList {
public static class ListNode {
public int val;
public ListNode next;
public ListNode(int data) {
this.val = data;
}
}
public static ListNode mergeTwoSortedLinkedList(ListNode head1, ListNode head2) {
if (head1 == null || head2 == null) {
return head1 == null ? head2 : head1;
}
// 从两个有序的链表中找见头位置,谁小谁做头
ListNode head = head1.val <= head2.val ? head1 : head2;
ListNode cur1 = head.next;
ListNode cur2 = head == head1 ? head2 : head1;
ListNode pre = head;
while (cur1 != null && cur2 != null) {
if (cur1.val <= cur2.val) {
pre.next = cur1;
cur1 = cur1.next;
} else {
pre.next = cur2;
cur2 = cur2.next;
}
pre = pre.next;
}
pre.next = cur1 != null ? cur1 : cur2;
return head;
}
public static void main(String[] args) {
ListNode firstListNode = new ListNode(1);
firstListNode.next = new ListNode(3);
firstListNode.next.next = new ListNode(5);
ListNode secondListNode = new ListNode(1);
secondListNode.next = new ListNode(2);
ListNode head = mergeTwoSortedLinkedList(firstListNode, secondListNode);
while (head != null) {
System.out.print(head.val + " ");
head = head.next;
}
System.out.println();
}
}
阅读量:896
点赞量:0
收藏量:0