1. 位运算(&、|、^、~、>>、<<)的学习,二进制数、原码、补码、反码,位运算在集合上的应用。
2. 链表的相关知识
1.零钱兑换
/*
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
*/
public static void main(String[] args) {
coinChange(new int[]{1, 2, 5}, 11);
coinChange(new int[]{2}, 3);
coinChange(new int[]{1}, 0);
}
public static int coinChange(int[] coins, int amount) {
Integer max = Integer.MAX_VALUE;
//1.dp[j]含义:装满容量为j的背包最少用几个物品
int[] dp = new int[amount + 1]; //3.dp数组初始化
/* 初始化dp数组为最大值 */
for (int j = 0; j < dp.length; j++) {
dp[j] = max;
}
dp[0] = 0; //金额为0时所需硬币数量也为0
for (int i = 0; i < coins.length; i++) { //4.遍历顺序:本体遍历顺序无所谓,因为无论是排列数还是组合数都不影响dp[j]
for (int j = coins[i]; j <= amount; j++) {
if (dp[j - coins[i]] != max) { //dp[j-coins[i]]不是初始最大值时
dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1); /* 2.递推公式 */
}
}
}
System.out.println(dp[amount] == max ? -1 : dp[amount]);
return dp[amount] == max ? -1 : dp[amount];
}
2.只出现一次的数字
/*
给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
*/
public static void main(String[] args) {
singleNumber(new int[]{2, 2, 1});
singleNumber(new int[]{4, 1, 2, 1, 2});
singleNumber(new int[]{1});
}
//假设数组有 2m+1 个数,其中有m个数个出现两次,1个数只出现1次。
//根据 结合律 和 异或的性质,数组中的全部元素的异或运算结果 即为 数组中只出现一次的数字。
public static int singleNumber(int[] nums) {
int single = 0;
for (int num : nums) {
single ^= num;
}
System.out.println(single);
return single;
}
3.只出现一次的数字Ⅱ
/*
给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
*/
public static void main(String[] args) {
singleNumber(new int[]{2, 2, 1});
singleNumber(new int[]{4, 1, 2, 1, 2});
singleNumber(new int[]{1});
}
//假设数组有 2m+1 个数,其中有m个数个出现两次,1个数只出现1次。
//根据 结合律 和 异或的性质,数组中的全部元素的异或运算结果 即为 数组中只出现一次的数字。
public static int singleNumber(int[] nums) {
int single = 0;
for (int num : nums) {
single ^= num;
}
System.out.println(single);
return single;
}
ListNode类(以下题目会用到)
public class ListNode {
public int val;
ListNode next;
public ListNode() {
}
public ListNode(int val) {
this.val = val;
}
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
4.移除链表元素
/*
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
*/
//递归
public ListNode removeElements(ListNode head, int val) {
if (head == null) {
return head;
}
head.next = removeElements(head.next, val);
return head.val == val ? head.next : head;
}
5.反转链表
/*
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
*/
// pre、cur双指针,pre指向null,cur指向head,不断遍历cur
//每次迭代到cur都让cur.next指向pre,然后cur和pre前进1位,直到cur指向null、pre指向最后一个结点
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
while (cur != null) {
ListNode temp = cur.next; //记录当前结点的下一个结点
cur.next = pre; //让当前结点指向pre
pre = cur; //pre和cur都前进1位
cur = temp;
}
return pre;
}
6.设计链表
class MyLinkedList {
int size;
ListNode head;
public MyLinkedList() {
size = 0;
head = new ListNode(0);
}
public int get(int index) {
if (index < 0 || index >= size) {
return -1;
}
ListNode cur = head;
for (int i = 0; i <= index; i++) {
cur = cur.next;
}
return cur.val;
}
public void addAtHead(int val) {
addAtIndex(0, val);
}
public void addAtTail(int val) {
addAtIndex(size, val);
}
public void addAtIndex(int index, int val) {
if (index > size) {
return;
}
index = Math.max(0, index); //不让下标为负
ListNode pre = head;
for (int i = 0; i < index; i++) {
pre = pre.next;
}
ListNode node = new ListNode(val);
node.next = pre.next; //插入新节点,先改变插入结点
pre.next = node;
size++; //链表长度+1
}
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
ListNode pre = head;
for (int i = 0; i < index; i++) {
pre = pre.next;
}
pre.next = pre.next.next;
size--; //删除结点,链表长度-1
}
}
7.两两交换链表中的节点
/*
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。
你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
*/
public ListNode swapPairs(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode newHead = head.next; //交换前
head.next = swapPairs(newHead.next); //除了head(头结点)和 newHead(新头结点),其余结点开始两两交换
newHead.next = head; //交换head(头结点)和 newHead(新头结点)
return newHead; //返回新的头结点
}
8.移除链表的倒数第N个节点
/*
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
*/
//暴力求解:1.计算链表的长度 2.删除第(length-n+1)个结点(结点数从1开始)
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0, head); /* 添加1个哑结点,该结点的next指针指向头结点 */
int length = getLength(head);
ListNode cur = dummy;
for (int i = 0; i < length - n + 1; i++) {
cur = cur.next;
}
cur.next = cur.next.next; //删除第n-1个结点
return dummy.next; //返回头结点
}
public int getLength(ListNode head) {
int length = 0;
while (head != null) {
length++;
head = head.next;
}
return length;
}