三、单链表
单链表
简单介绍:
- 链表是以节点的方式链式存储的
- 每个节点包含data域、next域:指向下一个节点
- 链表的各个节点不一定是连续存储的
- 链表分为带头节点的链表和没有头节点的链表,根据实际需求来确定
实例分析
将水浒传中的英雄作为节点,关于单链表的增删查操作
public class SingleLinkedListDemo {
public static void main(String[] args) {
//进行测试
HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.addByOrder(hero4);
singleLinkedList.addByOrder(hero2);
singleLinkedList.addByOrder(hero1);
singleLinkedList.addByOrder(hero3);
singleLinkedList.list();
System.out.println("删除一个");
singleLinkedList.delete(1);
singleLinkedList.list();
}
}
//定义一个HeroNode,每个HeroNode对象就是一个节点
class HeroNode {
public int no;
public String name;
public String nickName;
public HeroNode next;//指向下一个节点
public HeroNode(int no, String name, String nickName) {
this.no = no;
this.name = name;
this.nickName = nickName;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
//定义一个SingleLinkedList 管理我们的英雄
class SingleLinkedList {
//先初始化一个头节点,不存放任何数据
private HeroNode head = new HeroNode(0, "", "");
/*添加方法(不考虑编号的顺序)
*思路:
* 1、找到当前链表的最后节点
* 2、将最后这个节点的next 指向 新的节点
*/
public void add(HeroNode heroNode) {
//定义一个辅助节点
HeroNode temp = head;
//遍历链表
while (true) {
//找到了最后一个节点
if (temp.next == null) {
break;
}
temp = temp.next;
}
temp.next = heroNode;
}
/**
* 添加方法(考虑编号的顺序)
* 1、首先找到新节点添加的位置
* 2、新的节点的next等于temp.next
* 3、将temp.next等于新的节点
*/
public void addByOrder(HeroNode heroNode) {
HeroNode temp = head;
//标识新添加的编号是否存在
boolean flag = false;
while (true) {
if (temp.next == null) {
//temp已经到链表的最后
break;
}
if (temp.next.no > heroNode.no) {
break;
} else if (temp.next.no == heroNode.no) {
//说明标号已经存在
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
throw new RuntimeException(heroNode.no + " 编号已经存在!");
} else {
heroNode.next = temp.next;
temp.next = heroNode;
}
}
/*删除
* 1、找到需要删除的这个节点的前一个节点
* 2、temp.next = temp.next.next;
*/
public void delete(int no) {
HeroNode temp = head;
boolean flag = false;//是否找到待删除节点
while (true) {
if (temp.next == null) {
break;
}
if (temp.next.no == no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
temp.next = temp.next.next;
} else {
throw new RuntimeException("需要删除的节点不存在");
}
}
/**
* 遍历显示链表
*/
public void list() {
if (head.next == null) {
throw new RuntimeException("链表为空!");
}
HeroNode temp = head.next;
while (true) {
if (temp == null) {
break;
}
System.out.println(temp);
temp = temp.next;
}
}
/**
*
* @param head 链表的头节点
* @return 返回的就是有效节点的个数
*/
public static int getLength(HeroNode head){
if (head.next == null){
return 0;
}
int length = 0;
//定义一个辅助的变量
HeroNode cur = head.next;
while(cur!=null){
length++;
cur = cur.next;
}
return length;
}
}
面试题
1、查找单链表中的倒数第k个节点
/**
* 查找单链表中的倒数第k个节点
* 思路:
* 1、编写一个方法,接收head节点,同时接收一个index
* 2、index 表示是倒数第index个节点
* 3、先把链表从头到尾遍历,得到链表的总的长度
* 4、从链表的第一个开始遍历(size-index)个
* 5、如果找到了,则返回该节点,否则返回null
*/
public static HeroNode findLastIndexNode(HeroNode head,int index){
if (head.next == null){
return null;
}
int size = getLength(head);
if (index<=0||index>size){
throw new RuntimeException("传入参数范围误!");
}
HeroNode temp = head.next;
for (int i = 0; i < size-index; i++) {
temp = temp.next;
}
return temp;
}
2、单链表的反转
/**
* 单链表的反转
* 思路:
* 1、先定义一个新的节点reverseHead
* 2、从头到尾遍历原来的链表,每遍历一个节点,就将其取出来,并放在reverseHead的后面
* 3、然后原来链表的head.next = reverseHead.next;
*/
public static void reverseList(HeroNode head){
if (head.next == null || head.next.next == null){
return ;
}
HeroNode cur = head.next;
HeroNode next = null;//指向当前节点的下一个节点
HeroNode reverseHead = new HeroNode(0,"","");
//遍历原来的链表
while (cur!=null){
next = cur.next;
cur.next = reverseHead.next;//将cur的下一个节点指向新的链表的最前端
reverseHead.next = cur;
cur = next;
}
head.next = reverseHead.next;
}
3、逆序打印单链表
/**
* 逆序打印单链表
* 思路:使用栈的性质
*/
public static void reversePrint(HeroNode head){
if (head.next == null){
return;
}
HeroNode cur = head.next;
Stack<HeroNode> stack = new Stack<HeroNode>();
while (cur!=null){
stack.push(cur);
cur = cur.next;
}
while (stack.size()>0){
System.out.println(stack.pop());
}
}