排序算法总结:不具备稳定性的排序:选择排序、快速排序、堆排序
具有稳定性的排序:冒泡排序、插入排序、归并排序、一切桶排序思想下的排序
排序算法稳定性的证明:
冒牌排序实现稳定性:当相等的时候不发生交换
插入排序实现稳定性:相等时插到后面
归并排序:相等时,右边的放到右边
快排:partation是则是进行了选择排序,选择排序必然不能实现稳定性
堆排:根据二叉树排序,二叉树的排序直接破坏稳定性
桶排序:不基于比较的排序都比较容易实现稳定性
时间复杂度 | 空间复杂度 | 稳定性 | |
选择排序 | N*N | 1 | 不稳定 |
冒泡排序 | N*N | 1 | 稳定 |
插入排序 | N*N | 1 | 稳定 |
归并排序 | NlogN | N | 稳定 |
堆排序 | NlogN | 1 | 不稳定 |
01标准的经典快排没有办法保证稳定性,需要使用01 stable sort的算法,但实现起来非常的困难。
系统所实现的array.sort排序中,如果是基本类型采用快排,如果是非基础类型采用归并的原因是基础类型不需要稳定性而非基础类型默认需要稳定性。
哈希表:哈希表中的增删改查时间复杂度都是常数级别,但这个常数时间比一般的数组寻址时间大得多。
哈希表:hashset:key是基础类型,按值传递
hashmap:key不是基础类型,key和value是伴随共生,按引用类型传递
有序表:因为是有序组织,可以直接返回最大最小值
增删改查都是NlogN级别。
放入基础类型,按值传递;放入非基础类型,按地址传递,但是必须加入比较器,因为是有序组织
单链表问题:
公共链表问题:coding问题
面试时链表解题的方法论:对于笔试,不用在乎空间复杂度,一切为了时间复杂度
对于面试,时间复杂度依然放在第一位,但是要选用空间最省的方法。
技巧:使用额外的数据结构记录(eg:hash表);在遍历中使用快慢指针
题目一:判断链表是否为回文结构:给定一个单链表的头节点head,判断该链表是否为回文结构
方法一:使用栈实现,使用栈弹出与原来的链表进行比较
方法二:快慢指针,快指针一次走两步,满指针一次走一步(注意边界问题)
使用快慢指针解决问题:
import java.util.*;
import java.lang.*;
import java.io.*;
/* Name of the class has to be "Main" only if the class is public. */
public class IsPalindrome{
public static boolean isPalindrome(Node head){
if(head==null||head.next==null){
retuern;
}
Node n1=head;
Node n2=head;
while(n2.next!=null&&n2.next.next!=null){
n1=n1.next;
n2=n2.next.next;
}
//断开前后两节
n2=n1.next;
n1.next=null;
Node n3 = null;//n3为后半截逆序需要的辅助节点
while(n2!=null){
n3=n2.next;
n2.next=n1;
n2=n3;
n1=n2;
}
n3=n1;//n3保存最后的节点,作为后面后半截逆序需要
n2=head;
boolean result = true;
//开始遍历遍历比较
while(n1!=null&&n2!=null){
if(n1.value!=n2.value){
result=false;
return result;
}
n1=n1.next;
n2=n2.next;
}
//遍历结束,恢复原链表
Node n4 = n2;
while(n3!=null){
n2=n3.next;
n3.next=null;
n2.next=n3;
n3=n2;
}
n3.next=n4;
return result;
}
}
题目2:单链表按照某值划分为左边小,中间相等,右边大的形式:给定一个单链表的头节点head,节点的值类型是整型,再给定一个整数pivot。实现一个调整链表的函数,将链表调整为左边的值都是小于pivot的节点,中间部分都是等于pivot的节点,右边部分都是大于pivot的节点。要求调整后所有小于等于大于pivot的节点之间的相对顺序和调整前一样,时间复杂度达到O(N),额外空间复杂度达到O(1)。
简单版思路:将链表的值放到一个数组中,进行partation,然后再根据值把链表串联起来。
进阶版思路,设置六个指针,分别为小于区头尾,等于区头尾,大于区头尾,然后进行遍历,最后将各区串联起来(难点在于解决边界问题,如某个区为空的解决方法。 )
题目:复制含有随机指针节点的链表:一种特殊的单链表节点类描述如下:
class Node{
int value;
Node next;
Node rand;
Node(int val){
value = val;
}
}
rand指针是单链表节点结构中新增的指针,rand可能是指向链表中的任意一个节点,可能指向null,给定一个有Node节点类型组成的无环单链表的头节点head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头节点
1、使用hashmap,将所有的节点按key复制一遍到map中,然后遍历原来的链表,将原来链表中的next和rand复制到map中对应的Node中
2、不使用hashmap:复制新的节点到原来节点的后面(a.next=A.next;a=A.next),然后每次遍历一对A和a,把rand和value解决掉,最后在把新节点的next串联在一起,返回head',即可实现。