面试题1:删除值为key的所有节点 要求只遍历一遍
public Node removeAllKey(int key) {
if (this.head == null) {
return null;
}
Node prev = this.head;
Node cur = this.head.next;
while (cur != null) {
if (cur.data == key) {
prev.next = cur.next;
cur = cur.next;
} else {
prev = cur;
cur = cur.next;
}
}
if (this.head.data == key) {
this.head = this.head.next;
}
return head; // 返回的是一个头节点 head
}
面试题2: 反转单链表
public Node reverseList() {
Node prev = null;
Node cur = this.head;
Node curNext = null;
Node newHead = null;
while (cur != null) {
curNext = cur.next;
if (curNext == null) {
newHead = cur;
}
cur.next = prev;
prev = cur;
cur = curNext;
}
return newHead;
}
面试题3: 查找链表的中间节点 如果是偶数个 输出第二个
public Node middleNode() {
Node fast = this.head;
Node slow = this.head;
if (this.head == null) {
return null;
}
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
面试题4:找出链表的倒数第K个节点 遍历一次单链表
public Node FindKthTOTail(int k) {
if (this.head == null) {
return null;
}
if (k < 1) {
System.out.println("K是不合法的");
return null;
}
Node fast = this.head;
Node slow = this.head;
while (k - 1 > 0) {
if (fast.next != null) {
fast = fast.next;
k--;
} else {
System.out.println("没有这个节点");
return null;
}
}
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
以定值x为基准将链表分割成俩部分,所有小于x的节点排在大于或等于x的节点之前,保持顺序不变
思路:1、俩个线段开始还有结束 bs be as ae = null
2、定义一个cur遍历原来的单链表
3、如果 cur.data < x 放到第一个线段 , 如果相反,放到第二个线段 采用尾插法
4、当cur为空的时候 原来的单链表就遍历完了
注意: 1、第一个线段没有数据 返回as
2、be 和 as 拼接 be.next = as;
3、判断第一次插入 as = cur ; ae = cur;
public Node partition(int x){
Node bs = null;
Node be = null;
Node as = null;
Node ae = null;
Node cur = this.head;
while (cur != null){
if (cur.data < 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;
}
//判断 bs 是否为空 如果 bs == null 返回as
//如果 bs不为空 需要进行拼接
//如果ae不为空, ae.next = null be.next = as;
if (bs == null){
return as;
}
be.next = as;
if (ae != null){
ae.next=null;
}
return bs;
}
面试题7:删除排序的链表中重复的节点
public Node deleteDuplication(){
Node cur = this.head;
Node newHead = new Node(-1);
Node tmp = newHead ;
while (cur != null){
if (cur.next!=null &&cur.data==cur.next.data){
while (cur.next != null &&cur.data == cur.next.data){
cur = cur.next;
}
cur = cur.next; // 多走一步
}else {
tmp.next =cur;
tmp = tmp.next;
cur = cur.next;
}
}
tmp.next = null ;
return newHead.next;
}
面试题8:链表的回文结构
思路://1.找到中间节点
//2,开始反转单链表
//3.一个从头走 一个从尾走
public boolean chkPalindrome(){
Node fast = this.head;
Node slow =this.head;
if (this.head==null){
return false;
}
// if (this.head==null){ //在牛客上 没有规定为空会怎样
// return true ;
// }
if (fast!= null && fast.next!=null){ // 找到中间节点
fast = fast.next.next;
slow = slow.next;
}
Node cur = slow.next;
while (cur != null){ // 开始反转后半部分单链表 此时slow在中间位置
Node curNext =cur.next;
cur.next =slow;
slow =cur;
cur = curNext;
} //此时slow是最后一个节点 开始第三步
while (this.head != slow){ //一个从头走 一个从尾走
if (this.head.data != slow.data){
return false;
}
if (this.head.next==slow){ //判断偶数的情况
return true;
}
head = head.next;
slow = slow.next;
}
return true;
}
面试题10 给定一个链表,判断链表中是否有环
思路:定义 fast slow 速度不一样
* 如果有环 会相遇 如果没环 不会相遇
* 快的一次俩步 慢的一次一步
* 否则 要么很慢相遇 要么永远相遇不了
public boolean hasCycle(){
Node fast = this.head;
Node slow = this.head;
while (fast != null && fast.next != null){
fast=fast.next.next;
slow=slow.next;
if (fast==slow){
return true;
}
}
return false;
}
面试题11:给定一个链表,返回链表开始入环的第一个节点,如果链表无环,返回 null
思路:设 头节点距离入口节点 为 x
在环内 距 入口节点 y处相遇 环长为 c
fast多走一圈相遇时 慢引用路程 X+(C-Y)
快引用 X+C+(C-Y)
快引用路程是慢引用的二倍
2*[X+(C-Y)] = X+C+(C-Y)
化简得 X= Y
此时圈小,但是为啥一定在慢的走第一圈就遇到呢?不可以是慢的走第二圈吗?
fast多走 n 圈相遇时 慢引用路程 X+nC+(C-Y)
快引用 X+nC+(C-Y)
快引用路程是慢引用的二倍
2*[X+(C-Y)] = X+nC+(C-Y)
化简得 X-Y= nC-C
X = (n-1)C+Y
// 相遇后 fast 走n圈 加 y 等于slow从头节点出发
相遇时正好在入环点
化简后 可知 相遇节点 顺时针 距离 入口节点 为 x
于是 头节点 和 相遇节点 一人一步 便是 入口节点
public Node detectCycle(){
Node fast = this.head;
Node slow = this.head;
while (fast!=null && fast.next!=null) {
fast = fast.next.next;
slow = slow.next;
if (slow == fast) {
break;
}
}
if (fast==null || fast.next==null){
return null;
}
slow =this.head;
while (fast!= slow){
slow= slow.next;
fast = fast.next;
}
return slow;
}
面试题9:输入俩个链表,找出他们的第一个公共节点
思路: 求俩个单链表的长度 pl指向长的链表 ps指向短的链表
2. 计算长度的差值 让长的先走差值步
public static Node getIntersectionNode(Node headA ,Node headB){
int lenA = 0;
int lenB = 0;
Node pl = headA ;
Node ps = headB ;
while ( pl !=null ){
lenA++;
pl = pl.next;
}
while (ps !=null){
lenB++;
ps = ps.next;
}
pl = headA;
ps = headB;
int len = lenA - lenB;
if (len < 0) {
pl = headB;
ps = headA;
len = lenB - lenA ;
}
// 1. pl指向最长的单链表
for (int i = 0; i < len ; i++) {
pl = pl.next;
}
// 2. ps 和 pl 在同一个起跑线上
while (ps != pl && pl!=null && ps != null){
ps = ps.next;
pl = pl.next;
}
if (pl == ps && pl !=null && ps!=null){
return pl;
}
return null;
}
面试题5: 合并俩个有序链表
public static Node mergeTwoLists(Node headA,Node headB){
Node newHead = new Node(-1);
Node tmp = newHead;
while (headA!=null && headB!=null){
if (headA.data < headB.data){
tmp.next = headA;
tmp = tmp.next;
headA = headA.next;
}else {
tmp.next = headB;
tmp = tmp.next;
headB = headB.next;
}
}
if (headA==null){
tmp.next = headB;
}
if (headB==null){
tmp.next = headA;
}
return newHead.next;
}