堆排序:
堆排序的思想就是先将待排序的序列建成大根堆,使得每个父节点的元素大于等于它的子节点。此时整个序列最大值即为堆顶元素,我们将其与末尾元素交换,使末尾元素为最大值,然后再调整堆顶元素使得剩下的 n-1n−1 个元素仍为大根堆,再重复执行以上操作我们即能得到一个有序的序列。
public int[] heapSort(int[] nums){
if (nums==null||nums.length<2){
return nums;
}
for (int i=0;i<nums.length;i++){
heapInsert(nums,i);
}
int heapSize=nums.length;
swap(nums,0,--heapSize);
while (heapSize>0){
heapify(nums,0,heapSize);
swap(nums,0,--heapSize);
}
return nums;
}
public void heapInsert(int[] nums,int index){
while (nums[index]>nums[(index-1)/2]){
swap(nums,index,(index-1)/2);
index=(index-1)/2;
}
}
public void heapify(int[] nums,int index,int heapSize){
int left=index*2+1;
while (left<heapSize){//有左子树
int largest=left+1<heapSize&&nums[left]<nums[left+1]?left+1:left;
largest=nums[index]>nums[largest]?index:largest;
if (largest==index){
break;
}
swap(nums,largest,index);
index=largest;
left=index*2+1;
}
}
归并排序利用了分治的思想来对序列进行排序。对一个长为 n 的待排序的序列,我们将其分解成两个长度为 n/2,的子序列。每次先递归调用函数使两个子序列有序,然后我们再线性合并两个有序的子序列使整个序列有序。
public int[] mergeSort(int nums[],int l,int r){
if (l==r){//只有一个元素没有必要去比较
return null;
}
int mid=l+((r-l)>>1);
mergeSort(nums,l,mid);//分治
mergeSort(nums,mid+1,r);
merge(nums,l,mid,r);//合并
return nums;
}
private void merge(int[] nums, int l, int mid, int r) {
int[] help=new int[r-l+1];
int i=0;
int p1=l;
int p2=mid+1;
while (p1<=mid&&p2<=r){
help[i++]=nums[p1]<nums[p2]?nums[p1++]:nums[p2++];
}
while (p1<=mid){
help[i++]=nums[p1++];
}
while (p2<=r){
help[i++]=nums[p2++];
}
for (int j=0;j<help.length;j++){
nums[l+j]=help[i];
}
}
方法一:利用哈希表的存储特性
优点:思路清晰简单
缺点:需要额外的空间(哈希表)
class Solution {
public Node copyRandomList(Node head) {
//复制随机带指针的链表,我们可以利用哈希表的方式进行存储,key是原来的链表节点,value是复制的哈希表节点
//然后通过循环链表,从哈希表中取出对应的复制节点,从而赋值给next和random
HashMap<Node,Node> map=new HashMap<>();
Node cur=head;
while (cur!=null){//把所有节点全部压进哈希表
map.put(cur,new Node(cur.val));
cur=cur.next;
}
cur=head;
Node newHead=map.get(head);//新的头节点
Node last=newHead;//新链表的最后一个节点
for (;cur!=null;cur=cur.next){
last.next=map.get(cur.next);//next对应的复制节点
last.random=map.get(cur.random);
last=last.next;
}
return newHead;
}
}
方法二:合并再分割
public Node copyRandomList(Node head) {
//我们每遍历一个节点,就把他的复制节点插在它和它的next之中,那么我们复制节点的next就是原节点的next的下一个
//同样的random节点也可以利用这种方式
if (head==null){
return null;
}
Node cur=head;
while (cur!=null){
Node next=cur.next;//记录下一个节点
Node copyNode=new Node(cur.val);
cur.next=copyNode;
copyNode.next=next;
cur=next;//因为复制节点的加入,一次需要跳两格
}//循环结束说明链表的拼凑已经完成,我们需要对复制节点的random进行指向
cur=head;
while (cur!=null){
Node next=cur.next.next;//原本cur的下一个节点
cur.next.random=cur.random==null?null:cur.random.next;//把复制节点的random指向
cur=next;
}
cur=head;
Node newHead=cur.next;
while (cur!=null){//拆开链表
Node next=cur.next.next;
cur.next.next=next!=null?next.next:null;
cur.next=next;
cur=next;
}
return newHead;
}