对于该面试题,首先我们先创建一个单链表实现CRUD操作,由于我之前已写过对应的文章,现在下面附上链接,这里不再赘述。
对于该面试题,我们的思路是:
首先,获取到该单链表的有效数据的总数count,然后定义一个index=0的指针,且需要一个临时结点cur,cur指向当前遍历到的有效数据,通过循环,当index==(count-k)时,此时就可以得到确切位置的结点。详见代码如下:
package com.liu.linkedlist;
/**
* @author liuweixin
* @create 2021-09-07 14:46
*/
//新浪面试题:查找单链表中的倒数第k个结点
public class SinaLinkedList {
private Node1 head = new Node1("", 0);//头结点;
public static void main(String[] args) {
SinaLinkedList list = new SinaLinkedList();//创建一个链表
list.show();
list.addNode1(new Node1("a", 1));
list.addNode1(new Node1("b", 2));
list.addNode1(new Node1("c", 3));
list.addNode1(new Node1("d", 4));
list.addNode1(new Node1("e", 5));
list.show();
System.out.println("链表中的总数据数为:" + list.getCount());
int k = 3;
System.out.println("倒数第" + k + "个数据为:" + list.getReciprocal(k));
}
/**
* 获取单链表中的倒数第k个结点
*
* @param k 倒数第K个
*/
public Node1 getReciprocal(int k) {
int index = 0;//从正向开始
if (k > getCount() || k <= 0) {//判断输入的数是否合理,是否在有效数据的个数范围内
throw new RuntimeException("输入的数有误");
}
if (head.next == null) {
throw new RuntimeException("链表数据为空");
}
Node1 cur = head.next;//同样是借助辅助结点,指向第一个数据
while (true) {
if (index == (getCount() - k)) {//正向与 总数-倒数相等时即满足条件
return cur;
}
cur = cur.next;//指针后移
index++;//正向指针后移
}
}
/**
* 获取链表的总数
*
* @return 返回链表的总数
*/
public int getCount() {
int count = 0;
if (head.next == null) {//链表为空返回0
return 0;
}
Node1 cur = head;//创建一个辅助结点
while (cur.next != null) {//当cur的下一个结点不等于空时,count++,记录总数
count++;
cur = cur.next;//向下递换
}
return count;
}
/**
* 添加结点
*
* @param Node1 要加入的结点
*/
//对结点的操作放到链表中实现
public void addNode1(Node1 Node1) {
if (head.next == null) {
//只有一个头结点,可以直接把数据加入到链表中
head.next = Node1;
} else {
Node1 cur = head.next;//借助辅助结点去寻找到插入位置的前一个结点
while (cur.next != null && cur.next.no < Node1.no) {//当cur.next.no>Node1.no时退出循环,此时已找到插入位置的前一个结点。
//通过循环获取到插入位置的前一个结点
cur = cur.next;//cur需要下移,才能找到合适的位置
}
if (cur.next == null) {//找到链表的最后仍然没有比插入结点的no值大的,直接添加到链表的最后
cur.next = Node1;
return;
}
//此时cur.next.no>Node1.no,即找到插入位置的前一个结点
Node1.next = cur.next;//实现结点的传递
cur.next = Node1;
}
}
/**
* 删除链表的结点
*
* @param no 结点数据的编号
*/
public void deleteNode1(int no) {
if (head.next == null) {
System.out.println("链表为空,无法删除");
} else {
Node1 cur = head.next;//借助辅助结点去寻找到插入位置的前一个结点
while (true) {
if (cur.next == null) {//找到最后还没有找到删除的位置,说明该链表没有对应结点
System.out.println("链表无该节点,无法删除");
return;
}
if (cur.next.no == no) {
//此时已找到删除位置的前一个结点。
if (cur.next.next != null) { //删除结点的下一个结点不为空
cur.next = cur.next.next;
} else {//删除结点的下一个结点为空
cur.next = null;
}
return;//删除完成,结束方法
}
cur = cur.next;
}
}
}
/**
* 更改结点信息
*
* @param Node1 要更改的结点
*/
public void update(Node1 Node1) {
if (head.next == null) {
System.out.println("链表为空,无法更改");
} else {
Node1 cur = head;//借助辅助结点去寻找到插入位置的前一个结点
while (true) {
if (cur.next == null) {
if (cur.no != Node1.no) {
//找到最后还没有找到更新的位置,说明该链表没有对应结点
System.out.println("链表无该节点,无法更新");
}
return;
}
if (cur.next.no == Node1.no) {
//找到要更新的结点的前一个结点
if (cur.next.next != null) { //更新结点的下一个结点不为空
Node1.next = cur.next.next;
cur.next = Node1;//结点的移接
} else {//更新的结点是最后一个结点
cur.next = Node1;
}
return;
}
cur = cur.next;
}
}
}
/**
* 遍历链表数据
*/
public void show() {
if (head.next == null) {
System.out.println("该链表无数据");
return;
}
Node1 cur = head.next;
while (cur != null) {//注意这里的指针变化,这里的指针指的是当前遍历的结点,而不是遍历结点的前一个结点,这里跟上面添加和删除的临时结点不同
System.out.println(cur);
cur = cur.next;
}
}
}
class Node1 {//定义一个结点对象,每一个对象就是一个结点
Node1 next;
String name;
int no;
public Node1(String name, int no) {
this.name = name;
this.no = no;
}
@Override
public String toString() {
return "Node1{" +
" name='" + name + '\'' +
", no=" + no +
'}';
}
}