目录
7.整数反转
19. 删除链表的倒数第N个节点
20. 有效的括号
21:合并两个有序链表
23. 合并K个排序链表
61.旋转链表
148.排序链表
203. 移除链表中相同名称的元素
206. 反转链表
328 奇偶链表
7.Reverse 整数反转
1.用 % 的方法得到整数的最后一个数字 ,此时再用给定的整数 / 去除最后一个数字
如果整数还存在(不为0) 此时 重复之前的操作 将先前得到的数字*10 + 上最后一个数字
public static int reverse(int x) {
int ret=0;
while(x!=0){
int temp=ret*10+x%10;
if(temp/10!=ret||Integer.MIN_VALUE>ret || ret>Integer.MAX_VALUE) //temp/10 在java中
// 内存超限了不会报错 数值会减小
return 0;
ret = temp;
x=x/10;
}
return ret;
}
也可以用转换字符串的方法 将整数转换为字符串后再次进行整数倒置
public static int reverse(int x) {
int k=0;
String s= String.valueOf(x);
Long judege=0L;
String ss="";
int ans=0;
int length=s.length();
if(s.charAt(0)=='-'){
k=1;
ss+="-";
}
for(int i=length-1;i>=k;i--){
ss+=s.charAt(i);
}
judege=Long.parseLong(ss);
if(Integer.MIN_VALUE<=judege && judege<=Integer.MAX_VALUE){
return judege.intValue();
}else {
return 0;
}
}
dummy 哑巴
加入哑结点 防止极端情况删除第一个结点时没有前驱结点 做单独判断
方法1.查询一次链表的长度后,再次遍历删除倒数第n个结点
方法2.将查询到的结点放入进数组(集合)中,第二次直接查询出要删除结点的前驱结点(size-n-1) size为链表的长度,n为倒数第n个节点,数组查询的时间复杂度为o(1)
private ListNode removeNthFromEnd(ListNode head, int n) {
if(head.next==null)
return null;
List list = new ArrayList();
int i=0;
ListNode listnode = head;
while(listnode.next!=null){
list.add(listnode);
listnode=listnode.next;
}
list.add(listnode); //因为判断的是listnode.next!=null 最后一个结点还有加入
int l=list.size();
if(list.size()==n){ //将结点放在数组时 删除最后一个元素时情况要做出特殊处理 因为l-n-1会越界
head=head.next;
return head;
}
ListNode node=list.get(l-n-1); //l-n-1到删除结点的上一个结点
node.next=node.next.next;
return head;
}
方法3:比较巧妙,运用双指针l1,l2, 因为要删除倒数的结点,当l1指向链表的最后一个元素,此时l1-l2(index)索引位置为n
此时l2指向的元素就是需要删除元素的前驱结点 eg: a b c , n=2(表示此时要删除的元素为b), l1指向a ,l2指向c ,c-a=2;
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy=new ListNode(0);
dummy.next=head;
ListNode l1=dummy;
ListNode l2=dummy;
int i=0,j=0;
while(l1.next!=null){
if(j-i
括号匹配
IsValid
/**
* 用栈去解决问题
* 当遇到( [ { 时就入栈 当遇到右括号就出栈 并判断是否与栈值匹配
* 最后判断栈中是否为空 因为会遇到(( 此种情况
*/
public boolean isValid(String s) {
int length=s.length();
char []ch=new char[length];
int top=0;
char b ;
if(length==0||s==null){
return true;
}
for(int i=0;i
两个有序的数组或是链表进行合并 都可以用锯齿排序:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode l3=new ListNode(-1); //建立链表时一般创建一个哑结点,就不用单独处理第一个结点了
ListNode first = l3; //新创建一个引用操作链表,为了保存头节点
while(l1!=null&&l2!=null){ //此处最好不要用l->next 因为最后一个元素还没有判断
if(l1.val
用了分治算法,与21 合并两个有序链表 相像 但是直接用for循环调用合并函数会超时
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode l3=new ListNode(-1); //建立链表时一般创建一个哑结点,就不用单独处理第一个结点了
ListNode first = l3; //新创建一个引用操作链表,为了保存头节点
while(l1!=null&&l2!=null){ //此处最好不要用l->next 因为最后一个元素还没有判断
if(l1.val
/**
* 61. 旋转链表 思路和删除链表倒数结点 相同
取快慢指针i,j
使指针j-i相差n步
然后使最后的结点与头结点相连,使dummy.next连向 (最后的结点为第一个移动的元素 pre->next表示需要被移动的)最后一个元素,
after.next=head;
dummy.next=pre.next;
pre.next=null;
*/
public ListNode rotateRight(ListNode head, int k) {
ListNode dummy = new ListNode(-1);
int length=0;
ListNode pre=dummy,after=dummy,first=dummy; //因为设定了哑结点 所以不用判断空
int i=0,j=0;
dummy.next=head;
while(first.next!=null){
first=first.next;
length++;
}
while(after.next!=null){
if(j-i<(k%length)){
j++;
after=after.next;
}else{
i++;
pre=pre.next;
}
}
after.next=head;
dummy.next=pre.next;
pre.next=null;
return dummy.next;
}
一开始的思路 将链表放入数组中,进行分治算法的实现
改进后的思路 直接在链表上使用分治算法 ,只不过链表的分治算法
设定快慢指针,快指针一次走两个,慢指针一次走一个,最终能将链表分成相等的两段
/**
* 直接用链表进行分治排序
* 设定快慢指针,快指针一次走两个,慢指针一次走一个,最终能将链表分成相等的两段
* @param head
* @return
*/
public ListNode sortList(ListNode head) {
if(head==null){
return head;
}else{
return mergeSort(head);
}
}
public ListNode mergeSort(ListNode head){
if(head.next==null){ //递归返回条件 如果只有1个点就返回
return head;
}
ListNode pre=head;
ListNode after=head;
ListNode temp=null;
while(after!=null && after.next!=null){// 如果判断条件是after.next.next!=null 此时如果链表中只有三个点 temp会指向空指针
temp=pre;
pre=pre.next;
after=after.next.next;
}
temp.next=null;
ListNode l = mergeSort(head);
ListNode r = mergeSort(pre);
return merge(l,r);
}
public ListNode merge(ListNode l,ListNode r){
ListNode dummpy=new ListNode(-1);
ListNode first = dummpy;
while(l!=null && r!=null){
if(l.val<=r.val){
first.next=l;
first=first.next;
l=l.next;
}else{
first.next=r;
first=first.next;
r=r.next;
}
}
if(l!=null){
first.next=l;
}
if(r!=null){
first.next=r;
}
return dummpy.next;
}
public ListNode removeElements(ListNode head, int val) {
ListNode dummy=new ListNode(-1);
dummy.next=head;
ListNode first = dummy;
//思维导图 判断了结点相同 first是没有移动的 结点不同再移动指针
while(first.next!=null){
if(first.next.val!=val){
first=first.next;
}else{
first.next=first.next.next;
}
}
return dummy.next;
}
此题用1.递归的思想将链表倒转,先将链表遍历然后链表的指针倒置,使末尾结点变为首结点(同时作为返回条件),
未改良版递归(需要从末尾的结点将每一个数都遍历一次,再插入结点)(时间复杂度空间复杂度很高)实现:
public ListNode reverseList(ListNode head) {
ListNode l2 = new ListNode(0);
if(head==null) return null;
if(head.next==null){
l2=head;
return l2;
}else{
l2=reverseList(head.next);
ListNode first=l2;
while(first.next!=null){
first=first.next;
}
first.next=new ListNode(head.val); //递归时此处要注意 不能使first.next与此时递归的结点相同 因为结点并不包含一个值,而是多个值
return l2;
}
}
改进后的递归: 只改变指针方向就行
class Solution {
//递归 将链表的指向倒转 最后返回最后一个结点
public ListNode reverseList(ListNode head) {
ListNode l2 = new ListNode(0);
if(head==null||head.next==null){
return head;
}else{
l2=reverseList(head.next);
head.next.next=head; //将指针反转
head.next=null; //同时将正向的指针清除,防止形成循环
return l2;
}
}
}
2.迭代:
与递归(从最后更改链表指针的方向)相反,迭代是在遍历的时候就开始更改指针指向的方向
class Solution {
//迭代 将链表的指向倒转 最后返回最后一个结点
public ListNode reverseList(ListNode head) {
if(head==null||head.next==null){ // //所谓的空指针,就是指你的引用指向了没有分配内容的内
//这个时候就会报空指针异常,
return head;
}
ListNode pre = head;
ListNode after = head.next; //要做空指针判定
ListNode temp=null;
head.next=null; //已经保存了第一个和第二个结点,此时只需要将第二个结点的next连到第一//就行,然后 1结点到二结点的边断掉 不然会形成循环
while(after!=null){
temp=after.next;
after.next=pre;
// pre.next=null; 这个代码不能加 因为这个会断了之前after已经倒置的指针
pre=after;
after=temp;
}
return pre;
}
}
/**
* 328. 奇偶链表
方法一:
* 给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。
* 请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。
* 思路
* 定义变量 i 判断奇偶性
* 将偶数部分放到链表l1,偶数部分放到链表l2
* 然后l1的尾部连到l2的首部
* 最后返回l1的首部
*/
public ListNode oddEvenList(ListNode head) {
ListNode dummy = new ListNode(-1);
ListNode l1 = new ListNode(-1);
ListNode l2 = new ListNode(-1);
dummy.next=head;
ListNode first = dummy,first1=l1,first2=l2;
int i=0;
while(first.next!=null){
if(i%2==0){
i++;
first=first.next;
first1.next=new ListNode(first.val);
first1=first1.next;
}else{
i++;
first=first.next;
first2.next=new ListNode(first.val);
first2=first2.next;
}
}
first1.next=l2.next; //这里不要连错了 是l1的最后一个结点与l2的首结点相连
return l1.next;
}
方法2
但此题要求空间复杂度为1 因此直接在链表上进行奇偶结点的置换 关键要进行非空判断
改良版 直接在原链表进行奇偶结点交换
public ListNode oddEvenList(ListNode head) {
if(head==null || head.next==null)
return head;
// head 为奇链表头结点,o 为奇链表尾节点
ListNode o = head;
// p 为偶链表头结点
ListNode p = head.next;
// e 为偶链表尾节点
ListNode e = head.next;
while( e.next!=null&&o.next!=null ){
o.next=e.next;
o=o.next;
e.next=o.next;
e=e.next;
if(o.next==null)
break;
}
o.next=p;
return head;
}