目录
2.给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
4.将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的
5.编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前
9.给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL
一、链表题目
1.反转一个单链表
//反转一个单链表。
public Node reverseList(){
if (this.head==null){//无节点
return null;
}if (this.head.next==null){//只有一个节点
return head;
}
Node cur=head.next;//至少有两个节点
head.next=null;//将头节点置为空,
while (cur!=null){//使用尾插法,插入
Node curNxet=cur.next;
cur.next=head;
head=cur;
cur=curNxet;
}
return head;
}
2.给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
// 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点
public Node middleNode() {//找到链表的中间位置
Node slow = this.head;
Node fast = this.head;
while (fast != null && fast.next != null) {//个数是偶数和奇数
fast = fast.next.next;//fast走两步
slow = slow.next;//slow走一步
}
return slow;
}
3.输入一个链表,输出该链表中倒数第k个结点
//输入一个链表,输出该链表中倒数第k个结点
public Node findKthTOTail(int k){
if (k<0||head==null){//判断k是否合法
return null;
}
Node fast=head;
Node slow=head;
while (k-1>0) {//让fast走k步
if (fast.next==null){//判断fast.next是否为空
return null;
}
fast = fast.next;
/* if (fast==null){
return null;
}*/
k--;
}
while (fast.next!=null){//一起像前走
fast=fast.next;
slow=slow.next;
}
return slow;
}
4.将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的
//将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
public static Node mergeTwoLists(Node head1,Node head2) {
Node newNode = new Node(-1);//虚拟节点,数据不具备意义!!非常重要的方法
Node tmp = newNode;
while (head1 != null && head2 != null) {
if (head1.val < head2.val) {
tmp.next =head1;
tmp = tmp.next;
head1 = head1.next;
} else {
tmp.next = head2;
tmp = tmp.next;
head2=head2.next;
}
}
if (head1!=null){//两个链表可能长度不相同,判断谁的长
tmp.next=head1;
}if( head2!=null){
tmp.next=head2;
}
return newNode.next;
}
public void mytToString1(Node newHead) {
Node cur=newHead;
while (cur!= null) {
System.out.print(cur.val + " ");
cur = cur.next;//从当前节点指向下一个
}
System.out.println();
}
//调用的main方法
public static void main(String[] args) {
Test singleLinkedList1=new Test();
singleLinkedList1.addLast(13);
singleLinkedList1.addLast(21);
singleLinkedList1.addLast(32);
Test singleLinkedList2=new Test();
singleLinkedList2.addLast(15);
singleLinkedList2.addLast(25);
singleLinkedList2.addLast(53);
singleLinkedList2.addLast(83);
//ret是新的链表的头
Node ret= mergeTwoLists(singleLinkedList1.head,singleLinkedList2.head);
singleLinkedList1.mytToString1(ret);
5.编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前
//编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前
public Node partition(Node pHead, int x) {
if (pHead == null) {
return null;
}
Node bs = null;
Node be = null;
Node as = null;
Node ae = null;
Node cur = pHead;
while (cur != null) {
if (cur.val < x) {
if (bs == null) {
bs = cur;
be = cur;
}else {
be.next = cur;
be = be.next;
}
}else {
if (as == null) {//是不是第一次插入
as = cur;
ae = cur;
}else{
ae.next = cur;
ae = ae.next;
}
}
cur = cur.next;
}
if (bs == null) {
return as;
}
be.next = as;
//检查最后一段是否有数据,如果有需要把最后一个节点的next置为空,否则会出现死循环
if (as != null) {
ae.next = null;
}
return bs;
}
6.链表的回文结构
// 链表的回文结构
public boolean chk(){
if (this.head==null){//空链表
return true;
}if (this.head.next==null){//只有一个节点
return true;
}
//1.找到中间节点
Node fast = this.head;
Node slow = this.head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
//2.反转中间节点之后的链表
Node cur=slow.next;
while (cur!=null){
Node curNext=cur.next;
cur.next=slow;
slow=cur;
cur=curNext;
}
//3.slow到最后一个节点
while (head!=slow){
if (head.val!=slow.val){
return false;
} if (head.next==slow){//节点为偶数个
return true;
}
head=head.next;
slow=slow.next;
}
return true;
}
7.输入两个链表,找出它们的第一个公共结点
//输入两个链表,找出它们的第一个公共结点。
public Node geter(Node head1,Node head2){
if (head1==null||head2==null){
return null;
}
Node pl=head1;//使head1指向长的链表
Node ps=head2;//使head2指向短的链表
//1、求出两个链表的长度
int len1=0;
int len2=0;
while (pl!=null){
len1++;
pl=pl.next;
}
while (ps!=null){
len2++;
ps=ps.next;
}//走完之后已经为空了,需要重新指向
pl=head1;
ps=head2;
//2.已知链表长度,
int len=len1-len2;
if (len<0){
pl=head2;
ps=head1;//pl都是指向最长的,ps指向最短的
len=len2-len1;
}
//让最长的链表走len步
while (len!=0){
pl=pl.next;
len--;
}
//一人一步走,直到相遇
while (pl!=ps) {
pl = pl.next;
ps = ps.next;
}
//判断相遇时是否为空
if (pl!=null){
return pl;
}
return null;
}
8.给定一个链表,判断链表中是否有环。
//给定一个链表,判断链表中是否有环。
public boolean hasCycle(){
Node fast1=this.head;
//if (fast1==null){//没有节点
// return false;
//}
//if (fast1.next==null){//有一个节点
// return false;
//}
Node slow1=this.head;
while(fast1!=null&&fast1.next!=null){
fast1=fast1.next.next;//fast走两步
slow1=slow1.next;//slow走一步
if (fast1==slow1){
return true;
}
}
return false;
}
9.给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL
//给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回NULL
public Node hasNode() {
Node fast1 = this.head;
Node slow1 = this.head;
while (fast1 != null && fast1.next != null) {
fast1 = fast1.next.next;
slow1 = slow1.next;
if (fast1 == slow1) {
break;
}
}
if (fast1 == null || fast1.next == null) {
return null;
}
fast1 = head;
while (fast1 != slow1) {
fast1 = fast1.next;
slow1 = slow1.next;
}
return fast1;
}
二、无头双向链表实现
-
删除第一次出现关键字为key的节点
public void remove(int key) {
ListNode cur=this.head;
while (cur!=null){
if (cur.val==key){
if (cur==head){//删除的是头节点
head=head.next;
if (head!=null) {//来判断是不是只有一个节点
head.prev = null;
}
}else{//两种情况
cur.prev.next=cur.next;
}if (cur.next!=null) {//1.是中间节点
cur.next.prev = cur.prev;
}else {//2.是尾巴节点
last=last.prev;
}
return;
}
cur=cur.next;
}
}
三、ArrayList和LinkedList的区别
不同点
|
ArrayList
|
LinkedList
|
存储空间上
|
物理上一定连续 (底层为数组)
|
逻辑上连续,但物理上不一定连续 (底层为双层链表)
|
随机访问
|
支持
O(1)
|
不支持:
O(N)
|
头插
|
需要搬移元素,效率低
O(N)
|
只需修改引用的指向,时间复杂度为
O(1)
|
插入
|
空间不够时需要扩容
|
没有容量的概念
|
应用场景
|
元素高效存储
+
频繁访问
|
任意位置插入和删除频繁
|
链表适用于 经常对数据进行插入和删除的时候。