215.数组中的第K个最大元素
给定整数数组 nums
和整数 k
,请返回数组中第 k个最大的元素。
请注意,你需要找的是数组排序后的第 k
个最大的元素,而不是第 k
个不同的元素。
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
- 快排。唯一要注意的地方就是退出函数的条件,当待排序数组只有一个元素(k=1),那么结果就是它。这里没有考虑剪枝,即当前这一轮排序后确定的位置如果正好是第k个大的数字,则可以直接退出返回,不必进行到只剩一个元素待排序数组的情况。
public int quicksort(int[] nums,int low,int high,int k)
{
//if(low>=high)
//return ;
if(low==high)
return nums[high];
int base=nums[low];
int left=low,right=high;
while(low<high)
{
while(low<high&&nums[high]>=base)
high--;
while(low<high&&nums[low]<=base)
low++;
if(low<high)
{
int temp=nums[high];
nums[high]=nums[low];
nums[low]=temp;
}
}//这里循环出来必定有low=high
nums[left]=nums[high];
nums[high]=base;
//quicksort(nums,left,low-1);
//quicksort(nums,high+1,right);
if(right-high>=k)
return quicksort(nums,high+1,right,k);
else
return quicksort(nums,left,high,k-(right-high));
}
- 堆排序。整个堆排序巧妙的地方在于,大根堆的排序和每轮排序后结果(堆顶元素)都是保存在一个数组中。排序过程中堆空间变小,排序后的有序数组空间变大。因此自顶向下调整堆时,maxHeapify()的形参中要包含当前所要调整的堆的大小。其它要考虑清楚的就以下几点:
- 顺序存储的二叉树左孩子和右孩子表示方式。
- 堆调整时左孩子或者右孩子越界的限制条件。
- 初始堆时从哪个位置开始调整。
public int findKthLargest(int[] nums, int k) {
buildMaxHeap(nums);
int heapsize=nums.length;
for(int i=0;i<k;i++)
{
swap(nums,0,nums.length-1-i);
heapsize--; //排序完一轮堆大小减1
maxHeapif(nums,0,heapsize);
}
return nums[nums.length-k];
}
public void buildMaxHeap(int[] nums) //初始堆
{
for(int i=(nums.length-1)/2;i>=0;i--)
maxHeapif(nums,i,nums.length);
}
public void maxHeapif(int[] nums,int target,int heapsize)
{
int left=target*2+1,right=target*2+2,max=target;
if(left<heapsize&&nums[max]<nums[left])
max=left;
if(right<heapsize&&nums[max]<nums[right])
max=right;
if(max==target) //满足堆定义返回
return;
swap(nums,target,max);
maxHeapif(nums,max,heapsize);
}
25.K个一组翻转链表
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
- 第一步要先遍历一轮链表,找到最后一个不够k个节点的组的首节点,做法是设置一个计数器,每加到k就换一次首节点。这里注意遍历时候每次只能后移一个结点,跨两个在某些情况下容易出错。
- 第二步进行分组的反转,原理跟先前链表原地逆置差不多,只不过多了个分组,要把逆置后的组的尾节点设置为新的头结点(头插法),并且链接下一个组的头结点。phead是每个组的头结点,pend是每个组的尾结点。
public ListNode reverseKGroup(ListNode head, int k) {
ListNode p=head,rhead=null;
int group=1;
while(p!=null)
{
if(group==k)
{
rhead=p.next;
group=1;
p=p.next;
continue;
}
group++;
p=p.next;
}
ListNode phead=new ListNode(0,head),res=phead;
p=head;
group=0;
ListNode pnext,pend=null;
while(p!=rhead)
{
if(group==k)
{
phead=pend;
pend.next=p;
group=0;
}
pnext=p.next;
if(group==0)
{p.next=null;
pend=p;
}
else
p.next=phead.next;
phead.next=p;
p=pnext;
group++;
}
pend.next=rhead;
return res.next;
}
15.三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
- 先对数组进行排序,然后使用双指针法,这点非常难想到。因为只有数组有序才能使用双指针法,而双指针法是固定遍历子数组左端点nums[i],low=i,high=nums.length-1,通过移动low和high来使三数之和逼近零。只有当low和high相碰时才退出这一轮循环。
- 另一个难点在于去重。需要注意的是如果只去重不剪枝很可能会超时,因此把结果加入数组后,再通过while(nums[low]==nums[low-1])剪枝,这样子就不会超时了。
- new ArrayList<>(Arrays.asList(a,b,c)):通过反射将数组转换成集合 Arrays.sort(nums):排序
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res=new ArrayList<>();
for(int i=0;i<nums.length-2;i++)
{
if(nums[i]>0)
return res;
if(i>0&&nums[i]==nums[i-1])
continue;
int low=i+1,high=nums.length-1;
while(low<high)
{
while(low<high&&nums[i]+nums[low]+nums[high]<0)
low++;
while(low<high&&nums[i]+nums[low]+nums[high]>0)
high--;
if(low<high&&nums[i]+nums[low]+nums[high]==0)
{
res.add(new ArrayList<>(Arrays.asList(nums[i],nums[low],nums[high])));
while (low<high && nums[high] == nums[high- 1]) high--;
while (low<high && nums[low] == nums[low+1]) low++;
high--;
low++;
}
}
}
return res;}
21.合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
- 找出首元素更小的一个链表,让另一个链表插入其中。被插入的小链表要考虑前驱(可以new一个头结点更好处理),插入的大链表要考虑后继。
今日总结
刷题