链表
public class LinkedList {
private Node head = null;
//获取头指针
public Node getHead() {
return head;
}
//在尾部插入元素
public LinkedList add(int data) {
Node node = new Node(data);
if(head == null) {
head = node;
return this;
}
Node tmp = head;
while(tmp.next != null) {
tmp = tmp.next;
}
tmp.next = node;
return this;
}
//输出链表
public void print() {
Node tmp = head;
if (tmp == null) return;
while(tmp.next != null) {
System.out.print(tmp.data + ",");
tmp = tmp.next;
}
System.out.println(tmp.data);
}
//链表长度
public int length() {
Node tmp = head;
int i = 1;
while(tmp.next != null) {
tmp = tmp.next;
i++;
}
return i;
}
//移除i处元素
public void remove(int i) {
if (i==0) {
head = head.next;
return;
}
if (i >= length()) {
throw new IllegalArgumentException("越界");
}
Node tmp = head;
int n = 0;
while(head.next != null && n < i - 1) {
tmp = tmp.next;
n++;
}
if(tmp.next == null)
tmp.next = null;
else
tmp.next = tmp.next.next;
}
/**
*删除重复元素
*1.双重遍历 0(n^2)
*2.借用HASHTABLE
*/
public void deleteDuplecate() {
Hashtable<Integer,Integer> hashtable = new Hashtable<Integer, Integer>();
Node tmp = head;
Node pre = null;
while(tmp != null) {
if(hashtable.containsKey(tmp.data)) {
pre.next = tmp.next;
tmp = tmp.next;
}
else {
hashtable.put(tmp.data,1);
pre = tmp;
tmp = tmp.next;
}
}
}
//非递归反向链表
public void reverseIteratively() {
//非递归
Node pRecersedHead = head;
Node pNode = head;
Node pPrev = null;
while(pNode != null) {
Node pNext = pNode.next;
if(pNext == null) pRecersedHead = pNode;
pNode.next = pPrev;
pPrev = pNode;
pNode = pNext;
}
this.head = pRecersedHead;
}
//递归反向链表
public Node reverseIteratively(Node head) {
//递归
if(head == null || head.next == null) return head;
else {
Node newHead = reverseIteratively(head.next);
head.next.next = head;
head.next = null;
if(head == this.head) this.head = newHead;
return newHead;
}
}
//反向输出 借用栈实现 或者递归实现
public void printListReversely(Node node) {
if(node != null) {
printListReversely(node.next);
System.out.print(node.data);
}
}
//获取中间元素 使用两个指针,一个前进两步,一个前进一步
public Node getMid() {
Node p = head;
Node q = head;
while(p != null && p.next != null && p.next.next != null) {
p = p.next.next;
q = q.next;
}
return q;
}
}
判断括号字符串是否有效
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 注意空字符串可被认为是有效字符串。
示例 1:
输入: “()”
输出: true
示例 2:
输入: “()[]{}”
输出: true
示例 3:
输入: “(]”
输出: false
示例 4:
输入: “([)]”
输出: false
示例 5:
输入: “{[]}”
输出: true
解:
使用栈,若最后不为空或者出栈元素不对,则不匹配
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<Character>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if(c == '[' || c == '{' || c == '(') stack.push(c);
if(c == ']') if(stack.empty() || stack.pop() != '[') return false;
if(c == '}') if(stack.empty() || stack.pop() != '{') return false;
if(c == ')') if(stack.empty() || stack.pop() != '(') return false;
}
if(!stack.empty()) return false;
else return true;
}
}
参考解法:
使用字符串替换
public boolean isValid(String s) {
int length;
do {
length = s.length();
s = s.replace("()","").replace("{}","").replace("[]","");
} while (s.length() != length);
return s.length() == 0;
}
栈实现队列
使用栈实现队列的下列操作:- push(x) – 将一个元素放入队列的尾部。
- pop() – 从队列首部移除元素。
- peek() – 返回队列首部的元素。
- empty() – 返回队列是否为空。
示例:
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false
说明:
- 你只能使用标准的栈操作 – 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
- 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
- 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。
解:
使用两个栈,进队列时入栈1,出队列时若站栈2不为空则出栈,否则栈1全部元素入栈2
class MyQueue {
private Stack<Integer> stackIn = new Stack<Integer>();
private Stack<Integer> stackOut = new Stack<Integer>();
/** Initialize your data structure here. */
public MyQueue() {
}
/** Push element x to the back of queue. */
public void push(int x) {
stackIn.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
if(stackOut.empty()) {
while (!stackIn.empty()) {
stackOut.push(stackIn.pop());
}
}
return stackOut.pop();
}
/** Get the front element. */
public int peek() {
if(stackOut.empty()) {
while (!stackIn.empty()) {
stackOut.push(stackIn.pop());
}
}
return stackOut.peek();
}
/** Returns whether the queue is empty. */
public boolean empty() {
return stackIn.empty() && stackOut.empty();
}
}
返回数据流第k大
设计一个找到数据流中第K大元素的类(class)。注意是排序后的第K大元素,不是第K个不同的元素。你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数据流中的初始元素。每次调用 KthLargest.add,返回当前数据流中第K大的元素。
示例:
int k = 3;
int[] arr = [4,5,8,2];
KthLargest kthLargest = new KthLargest(3, arr);
kthLargest.add(3); // returns 4
kthLargest.add(5); // returns 5
kthLargest.add(10); // returns 5
kthLargest.add(9); // returns 8
kthLargest.add(4); // returns 8
解:
方法1:排序 O(Nklog(k))
方法2:维护含k个元素的小顶堆 O(Nlogk)
class KthLargest {
private int k = 0;
private PriorityQueue<Integer> priorityQueue;
public KthLargest(int k, int[] nums) {
this.k = k;
priorityQueue = new PriorityQueue<Integer>(k);
for(int i : nums) add(i);
}
public int add(int val) {
if(priorityQueue.size() < k) priorityQueue.add(val);
else if (val >= priorityQueue.peek()) {
priorityQueue.poll();
priorityQueue.add(val);
}
return priorityQueue.peek();
}
}
滑动窗口最大值
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。返回滑动窗口最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
解:
方法一:维护小顶堆(优先队列) O(Nlogk)
方法二:维护双向队列 O(N)
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length == 0) return new int[]{};
LinkedList<Integer> queue = new LinkedList<>();
int[] outPut = new int[nums.length - k + 1];
for (int i = 0; i < k-1; i++) add(queue,i,nums);
for (int i = k-1; i < nums.length; i++) {
add(queue,i,nums);
if(queue.peek() <= i-k) queue.poll();
outPut[i-k+1] = nums[queue.peek()];
}
return outPut;
}
private void add(LinkedList<Integer> queue,int i,int[] nums) {
while (!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]) queue.pollLast();
queue.addLast(i);
}
}
相交链表
编写一个程序,找到两个单链表相交的起始节点。解:
先判断是否相交
计算两者长度
长度差即两者头节点距离公共交点的距离差
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB == null) return null;
ListNode pA = headA;
ListNode pB = headB;
int lenA = 1;
int lenB = 1;
while (pA.next != null) {
pA = pA.next;
lenA++;
}
while (pB.next != null) {
pB = pB.next;
lenB++;
}
if(pA != pB) return null;
ListNode tA = headA;
ListNode tB = headB;
if(lenA > lenB) {
int d = lenA - lenB;
while(d!=0) {
tA = tA.next;
d--;
}
}else {
int d = lenB - lenA;
while (d!=0) {
tB = tB.next;
d--;
}
}
while(tA != tB) {
tA = tA.next;
tB = tB.next;
}
return tA;
}
}
有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的一个字母异位词。示例 1:
输入: s = “anagram”, t = “nagaram”
输出: true
示例 2:
输入: s = “rat”, t = “car”
输出: false
说明:
你可以假设字符串只包含小写字母。
进阶:
如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?
解:
方法1. 排序 然后比较排序好的字符串 O(nlog(n))
方法2. 使用Map计数 O(N)
public boolean isAnagram(String s, String t) {
Map<Character,Integer> maps = new HashMap<Character, Integer>();
Map<Character,Integer> mapt = new HashMap<Character, Integer>();
if(s.length() != t.length()) return false;
for (int i = 0; i < s.length(); i++) {
Character sc = s.charAt(i);
Character tc = t.charAt(i);
if(maps.containsKey(sc)) maps.put(sc,maps.get(sc)+1);
else maps.put(sc,1);
if(mapt.containsKey(tc)) mapt.put(tc,mapt.get(tc)+1);
else mapt.put(tc,1);
}
return maps.equals(mapt);
}
两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
解:
方法1. 暴力循环 O(N^2)
方法2. 使用Set O(N)
方法3. 两遍哈希表 一遍插入,一遍查找 O(N)
方法4. 一遍哈希表 插入同时查找
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return new int[] { map.get(complement), i };
}
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}
}
三数之和
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
解:
1.暴力循环 O(N^3)
2.使用Set O(N^2)
3.先排序O(Nlog(N)),后查找,枚举一个数,另外两个数从后面的数组两边往中间夹 O(N^2)
public List<List<Integer>> threeSum(int[] nums) {
Set<List<Integer>> set = new HashSet<>();
Arrays.sort(nums);
int length = nums.length;
for (int i = 0; i < length-2; i++) {
if (i != 0 && nums[i-1] == nums[i]) continue;
int min = i + 1;
int max = length-1;
while(min != max) {
if(nums[i] + nums[min] + nums[max] == 0) {
set.add(Arrays.asList(nums[i], nums[min], nums[max]));
min = min + 1;
} else if(nums[i] + nums[min] + nums[max] < 0)
min = min + 1;
else max = max - 1;
}
}
return new ArrayList<>(set);
}