46.全排序
思路:(递归)
对每一个元素进行递归,用一个used[]记录当前已经使用过的元素,当全部元素都被使用完则放入list里面记录当前的排序
代码:
public List<List<Integer>> permute(int[] nums) {
boolean used[]=new boolean[nums.length];
List<Integer> list=new ArrayList<Integer>();
List<List<Integer>> res=new ArrayList<List<Integer>>();
Arrays.sort(nums);
dfs(nums,used,list,res);
return res;
}
public void dfs(int nums[],boolean used[],List<Integer> list,List<List<Integer>> res) {
if(list.size()==nums.length){
res.add(new ArrayList<>(list));
return;
}
for(int i=0;i<nums.length;i++){
if(used[i])
continue;
//如果当前元素没有被选
//则选当前元素继续dfs
used[i]=true;
//将该元素添加进第一个list
list.add(nums[i]);
dfs(nums,used,list,res);
//当前元素使用完以后一定已经遍历成功了,所以需要将其恢复成未使用
used[i]=false;
//将list中的最后一个元素删除
list.remove(list.size()-1);
}
}
47.全排列2
思路:(递归)
和上面的很像,就是要加个判断条件,判断前一个元素和当前元素是否相等,相等就跳到下一个元素进行(因为是排好序的,所以如果前后两个元素相同就会跳到直到不相同的元素上)
代码:
public List<List<Integer>> permuteUnique(int[] nums) {
boolean used[]=new boolean[nums.length];
List<Integer> list=new ArrayList<Integer>();
List<List<Integer>> res=new ArrayList<List<Integer>>();
//排序后再遍历,从最小的元素开始
Arrays.sort(nums);
dfs(nums,used,list,res);
return res;
}
public void dfs(int nums[],boolean used[],List<Integer> list,List<List<Integer>> res){
if(list.size()==nums.length){
res.add(new ArrayList<>(list));
return;
}
for(int i=0;i<nums.length;i++){
if(used[i])
continue;
if(i>0&&nums[i-1]==nums[i]&&used[i-1])
//加一个判断当前元素和前一元素是否相等,相等就不考虑这个元素
continue;
used[i]=true;
list.add(nums[i]);
dfs(nums,used,list,res);
used[i]=false;
list.remove(list.size()-1);
}
}
32.最长有效括号
思路:(栈)
代码:
class Solution {
public int longestValidParentheses(String s) {
int maxans = 0;
Deque<Integer> stack = new LinkedList<Integer>();
stack.push(-1);//先压栈一个-1,防止等下弹出的时候栈空报错
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') {//左括号,压栈
stack.push(i);
} else {
stack.pop();//如果是右括号,则先出栈
//如果栈是空的,把右括号压栈
if (stack.isEmpty()) {
stack.push(i);
} else {//计算当前最大的长度
maxans = Math.max(maxans, i - stack.peek());
}
}
}
return maxans;
}
}
146.LRU缓存
思路:(hash表+双向链表)
使用hash表当缓存,双向链表用来将刚刚访问过或者添加进来的元素放到链表头部
代码:
class LRUCache {
class Node{
int key;
int value;
Node next;
Node pre;
Node(){}
Node(int key,int value){
this.key=key;
this.value=value;
}
}
private Map<Integer,Node> map=new HashMap<>();
private Node head;//头指针的next永远都指向第一个结点
private Node tail;//尾指针的pre永远指向最后一个结点
private int capacity;
public LRUCache(int capacity) {
this.capacity=capacity;
this.head=new Node();
this.tail=new Node();
head.next=tail;
tail.pre=head;
}
public int get(int key) {
int v;
if(map.containsKey(key)){
v=map.get(key).value;
}else{
return -1;
}
moveToHead(map.get(key));
return v;
}
public void put(int key, int value) {
Node node=map.get(key);
if(node==null){
Node newNode=new Node(key,value);
map.put(key,newNode);
addToHead(newNode);
if(this.capacity<map.size()){
Node last=removeTail();
map.remove(last.key);
}
}else{
node.value=value;
moveToHead(node);
}
}
public void remove(Node node){
node.next.pre=node.pre;
node.pre.next=node.next;
}
public void moveToHead(Node node){
remove(node);
addToHead(node);
}
public Node removeTail(){
Node last=tail.pre;
remove(last);
return last;
}
public void addToHead(Node node){
node.pre=head;
node.next=head.next;
head.next.pre=node;
head.next=node;
}
}
215.数组中的第k个最大元素
思路:(单指针)
先排序,然后从尾到头遍历
代码:
public int findKthLargest(int[] nums, int k) {
Arrays.sort(nums);
int count=0;
for(int i=nums.length-1;i>=0;i--){
count++;
if(count==k)
return nums[i];
}
return -1;
}
25.k个一组翻转链表
思路:(双指针)
将每找到的k个一组的链表断开,然后单独进行翻转,后面再和两边的链表相连
代码:
public ListNode reverseKGroup(ListNode head, int k) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode pre = dummy;
ListNode end = dummy;
while (end.next != null) {
for (int i = 0; i < k && end != null; i++) end = end.next;
if (end == null) break;
ListNode start = pre.next;
ListNode next = end.next;
//断开链表
end.next = null;
//放入翻转函数,并返回翻转后的头结点
pre.next = reverse(start);
start.next = next;
pre = start;
end = pre;
}
return dummy.next;
}
//翻转函数
private ListNode reverse(ListNode head) {
ListNode pre = null;
ListNode curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = pre;
pre = curr;
curr = next;
}
return pre;
}
15.三数之和
思路:(双指针)
代码:
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res=new ArrayList<>();
Arrays.sort(nums);
for(int first=0;first<nums.length-2;first++){
if(first>0&&nums[first]==nums[first-1])
continue;
if(nums[first]>0)
break;
int third=nums.length-1;
for(int second=first+1;second<nums.length-1;second++){
if(second>first+1&&nums[second]==nums[second-1])
continue;
while(third>second&&nums[first]+nums[second]+nums[third]>0)
third--;
if(third==second)
break;
if(nums[first]+nums[second]+nums[third]==0){
List<Integer> list=new ArrayList<>();
list.add(nums[first]);
list.add(nums[second]);
list.add(nums[third]);
res.add(list);
}
}
}
return res;
}
思路:(递归)(倒数第三个开始就超时了)
代码:
public List<List<Integer>> threeSum(int[] nums) {
List<Integer> list=new ArrayList<>();
Set<List<Integer>> set=new HashSet<>();
boolean []isused=new boolean[nums.length];
Arrays.sort(nums);
dfs(nums,0,set,list,isused,0);
List<List<Integer>> res=new ArrayList<>();
Iterator<List<Integer>> iterator = set.iterator();
while (iterator.hasNext()){
res.add(iterator.next());
}
return res;
}
public void dfs(int nums[],int count,Set<List<Integer>> set,List<Integer> list,boolean isused[],int ii)
{
if(count==3){
set.add(new ArrayList<>(list));
return;
}
for(int i=0;i<nums.length;i++){
if(isused[i])
continue;
if(count==2&&nums[i]+list.get(0)+list.get(1)!=0)
continue;
if(ii>i)
continue;
if(list.size()!=0&&list.get(0)>0)
return ;
list.add(nums[i]);
count++;
isused[i]=true;
dfs(nums,count,set,list,isused,i+1);
isused[i]=false;
count--;
list.remove(list.size()-1);
}
}
912.排序数组
思路:(快速排序)
将数组分成两个部分,选择一个基准值,数组的头部和尾部分别设置两个指针,遍历数组,将右指针下的元素值和当前左指针下的元素值交换,然后再去遍历左指针(左右指针交替遍历),然后将当前的基准值放到左指针和右指针重合的位置上。再对重合位置的两边的数组进行相同的操作。
参考博客:
Java实现快排(图文讲解)_ChenSeventeen的博客-CSDN博客_java快排
代码:
public int[] sortArray(int[] nums) {
int low=0;
int high=nums.length-1;
sort(nums,low,high);
return nums;
}
public void sort(int nums[],int low,int high){
//=就表示1个元素,没必要排序
if(low<high){
int part=paixu(nums,low,high);
sort(nums,low,part-1);
sort(nums,part+1,high);
}
}
public int paixu(int nums[],int low,int high){
int temp=nums[low];
while(low<high){
//高低位交替查询
while(low<high&&nums[high]>=temp)
high--;
nums[low]=nums[high];
while(low<high&&nums[low]<=temp)
low++;
nums[high]=nums[low];
}
nums[low]=temp;
return low;
}
思路:(归并排序)
先递归至底层,然后从底层开始排序,排序完以后就合并,一直到最高层就排序完了
代码:
public int[] sortArray(int[] nums) {
int low=0;
int high=nums.length-1;
merge(nums,low,high,new int[nums.length]);
return nums;
}
public void merge(int nums[],int low,int high,int temp[]){
if(low<high){
int mid=(high+low)>>1;
//先拆
merge(nums,low,mid,temp);
merge(nums,mid+1,high,temp);
//再并
sort(nums,low,mid,high,temp);
}
}
public void sort(int nums[],int low,int mid,int high,int temp[]){
int i=low;//数组左边部分指针
int j=mid+1;//数组右边部分指针
int index=0;//temp数组的指针
while(i<=mid&&j<=high){
if(nums[i]<=nums[j])
temp[index++]=nums[i++];
else
temp[index++]=nums[j++];
}
//把没有拷贝完数组那部分直接移到temp后面
while(i<=mid)
temp[index++]=nums[i++];
while(j<=high)
temp[index++]=nums[j++];
//将temp中的数组重新移到nums对应位置中
i=low;
index=0;
while(i<=high)
nums[i++]=temp[index++];
}
思路:(堆排序)
第一次先是从最后一个非叶子结点开始(nums.length/2-1),构建一个大/小顶堆。然后将root元素取出,并将堆中最后一个元素设置为root,再构建大/小顶堆,直到剩下最后一个元素。
代码:
public int[] sortArray(int[] nums) {
paixu(nums);
return nums;
}
//构建大顶堆
public void maxDui(int nums[],int size,int parent){
//定位到左节点
for(int i=2*parent+1;i<size;i=2*parent+1){
//如果右边节点大于左边节点,则将i指向右节点
if(i+1<size&&nums[i]<nums[i+1])
i+=1;
if(nums[i]>nums[parent]){
int temp=nums[parent];
nums[parent]=nums[i];
nums[i]=temp;
//继续往下面构造
parent=i;
}else
break;
}
}
public void paixu(int nums[]){
int len=nums.length;
//从最后一个非叶子节点开始构建大顶堆
for(int i=nums.length/2-1;i>=0;i--){
maxDui(nums,len,i);
}
//第一次构建完大顶堆后交换根节点和最后一个节点并再构建大顶堆
//直到剩下最后一个节点
while(len>0){
int temp=nums[0];
nums[0]=nums[len-1];
nums[len-1]=temp;
len--;
maxDui(nums,len,0);
}
}