链表是以节点的方式来存储的。
1.一个节点包含。 data域(用来存放数据),next域:指向下一个节点(存储的是内存的地址)。
2.各个节点不一定是连续存放的
3.链表分带头结点的链表和没有头节点的链表。
import java.util.Stack;
public class MyLinkedList {
//定义一个头节点。不存放具体的数据。
private User head = new User(0, "");
public User getHead() {
return head;
}
//添加节点到单向链表。
/**
* 默认排序添加思路:
* 1.找到当前链表的最后节点。
* 2.将最后节点的next指向新的节点
*/
public void add(User user) {
//因为head节点不可以动,因此需要创建一个辅助节点
User temp = head;
//循环查找。找到链表的最后
while (true) {
//找到链表的最后
if (temp.next == null) {
break;
}
//如果没有找到最后,将temp后移
temp = temp.next;
}
//把新的节点指向链表最后
temp.next = user;
}
/**
* 按照ID排序添加思路:
* 1.通过遍历找到新添加的节点的位置,
* 2.把新的节点.next 指向 temp.next
* 3.把temp.next指向新的节点。
*/
public void addByOrder(User user) {
//因为头节点不能动。因此需要通过一个辅助指针来帮助查找添加的位置。
//我们找的temp应该是位于添加位置的前一个节点。否则无法插入。
User temp = head;
while (true) {
//如果为null说明已经到最后了。这时候直接插入就可以了。
if (temp.next == null) {
break;
}
//如果当前节点的下一个节点的ID比新节点的ID大,则表示找到(新节点在这两个中间插入。)
if (temp.next.id > user.id) {
break;
} else if (temp.next.id == user.id) {
//如果编号存在 直接返回。不做插入操作
System.out.printf("编号%d存在了。无法添加。", user.id);
return;
}
temp = temp.next;
}
//.把新的节点.next 指向 temp.next
user.next = temp.next;
//把temp.next指向新的节点。
temp.next = user;
}
/**
* 修改节点思路:
* 1.找到和新节点相同ID的节点。 temp.id == 新节点.id
* 2.更改姓名 temp.name = 新节点.name;
*/
//修改节点信息。根据人物ID修改人物名称。
public void update(User newUser) {
//判空
if (head.next == null) {
System.out.println("链表为空。无法修改");
return;
}
//找到需要修改的节点,根据人物ID
//因为头节点不能动。所以定义一个辅助节点
User temp = head.next;
while (true) {
if (temp == null) {
break;
}
//如果找到则直接修改名字
if (temp.id == newUser.id) {
temp.name = newUser.name;
return;
}
//temp后移实现遍历
temp = temp.next;
}
System.out.printf("没有找到ID为%d的节点,无法修改", newUser.id);
}
/**
* 删除节点思路:(根据人物ID来删除)
* 1.先找到和要删除节点相同ID的前一个节点 temp.
* 2.temp.next = temp.next.next (上一个节点直接指向当前节点的下一个节点。)
* 3.被删除的节点,将不会有其他引用指向,会被垃圾回收机制回收。
*/
public void delete(int id) {
User temp = head;
boolean flag = false;
while (true) {
if (temp.next == null) {
break;
}
//找到了待删除节点的前一个节点temp。
if (temp.next.id == id) {
//待删除的上一个节点指向他的下一个节点(实现删除。)
temp.next = temp.next.next;
return;
}
//temp后移实现遍历
temp = temp.next;
}
System.out.printf("要删除的节点%d不存在", id);
}
/**
* 打印输出思路:
* 1.找到头节点的下一个节点。指向辅助节点temp
* 2.打印输出temp并将temp指向下一个节点。
* 3.循环第二步。
*/
//显示当前链表。
public void showList() {
//判断当前链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
//因为头节点不能动。因此需要一个辅助节点。
User temp = head.next;
while (true) {
//判断是否到最后
if (temp == null) {
break;
}
//输出节点信息
System.out.println(temp);
//temp后移实现遍历
temp = temp.next;
}
}
/**
* 面试题:获取单链表有效节点的个数(忽略头节点。)
*/
public static int getLength(User head) {
if (head.next == null) {
return 0;
}
int length = 0;
User temp = head.next;
while (temp != null) {
length++;
temp = temp.next;
}
return length;
}
/**
* 面试题:查找单链表中的倒数第N个节点
* 思路:
* 1.编写一个方法接受 单链表的head节点和index。
* index表示是倒数第index个节点
* 2.获取链表的长度length。
* 3.得到length后,从链表第一个开始遍历(lenth-index)个就是需要的节点。
* 4.如果找到返回该节点。找不到返回null
*/
public static User findLastIndexNode(User head, int index) {
//如果链表为空返回null
if (head.next == null) {
return null;
}
//获取链表的长度。调用上一个方法。
int length = getLength(head);
//遍历 length -index位置,就是倒数第index的节点。
if (index <= 0 || index > length) {
return null;
}
//定义给辅助变量,for循环定位到倒数的index
User temp = head.next;
for (int i = 0; i < length - index; i++) {
temp = temp.next;
}
return temp;
}
/**
* 面试题:实现单链表的翻转。
* 思路:
* 1.先定义一个节点 reverseHead = new User() reverseHead.next默认为null
* 2.从头到尾遍历原来的链表,每遍历一个节点,就将其next指向上一个节点(第一个会指向null),
* 然后取出放入新节点的next。
* 3.完成遍历后。把原来链表的head.next指向新节点的next即可。
*/
public static void reversetList(User head) {
//如果当前链表为空,或者只有一个节点。则无需反转
if (head.next == null || head.next.next == null) {
return;
}
//定义一个辅助节点。
User temp = head.next;
//指向当前节点的下一个节点。
User next = null;
//创建一个新节点。(相当于一个容器 用来暂时存放未翻转完的链表。)
User reverseHead = new User(0, "");
//遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead的最前端
while (temp != null) {
//先取出当前节点的下一个节点备用。否则设置next后会无法找到下一个节点。
next = temp.next;
//当前节点的next指向新节点(第一次会指向null)
temp.next = reverseHead.next;
//把翻转后的链表结构赋值给新节点
reverseHead.next = temp;
temp = next;
}
//最后完成翻转后。将链表的头节点指向新节点的next即可。
head.next = reverseHead.next;
}
/**
* 面试题:从尾到头打印单链表
* 思路:
* 1.方式1:将单链表进行反转操作,然后再遍历即可,这样做会破坏原来的单链表结构!不建议!!!
* 2.方式2:可以利用栈这个数据结构,将各个节点压如栈中。然后利用栈的先进后出的特点。实现逆序打印效果。
* 3.方式3:用递归打印。
*/
//利用栈来实现逆序打印。
public static void reversetPrint2(User head) {
Stack<User> stack = new Stack<>();
if (head.next==null){
return;
}
User temp = head.next;
while (temp!=null){
stack.add(temp);
temp = temp.next;
}
while (stack.size()>0){
System.out.println(stack.pop());
}
}
//利用递归来实现逆序打印。
public static void reversetPrint3(User head) {
if (head != null) {
reversetPrint3(head.next);
System.out.println(head);
}
}
/**
* 面试题:合并两个有序的单链表。合并之后链表依然有序。
*/
public static User mergeList(User user1,User user2){
if (user1==null)return user2;
if (user2==null)return user1;
User head = null;
if (user1.id<user2.id){
head =user1;
head.next = mergeList(user1.next,user2);
}else{
head =user2;
head.next = mergeList(user2.next,user1);
}
return head;
}
}
class User {
public int id;
public String name;
public User next;
public User(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
class TestList {
public static void main(String[] args) {
User user = new User(1, "张三");
User user1 = new User(2, "李四");
User user2 = new User(3, "王五");
User user3 = new User(4, "赵六");
MyLinkedList list2 = new MyLinkedList();
//倒序插入测试效果
list2.addByOrder(user3);
list2.addByOrder(user2);
list2.addByOrder(user1);
list2.addByOrder(user);
list2.addByOrder(user);
System.out.println("--------------");
list2.showList();
//修改节点的名字。
User newUser = new User(1, "一号人物");
list2.update(newUser);
System.out.println("修改后的链表。。。");
list2.showList();
//删除
// list2.delete(1);
// list2.delete(4);
System.out.println("删除后的链表。。。");
list2.showList();
//获取单链表的有效节点个数
System.out.println(MyLinkedList.getLength(list2.getHead()));
//获取倒数第1个节点
System.out.println(MyLinkedList.findLastIndexNode(list2.getHead(), 1));
//MyLinkedList.reversetList(list2.getHead());
//System.out.println("倒序后的列表~~");
//list2.showList();
System.out.println("栈打印");
MyLinkedList.reversetPrint2(list2.getHead());
System.out.println("递归打印");
MyLinkedList.reversetPrint3(list2.getHead().next);
User user10 = new User(1, "张一");
User user20 = new User(2, "李二");
User user30 = new User(3, "王三");
User user40 = new User(4, "赵四");
User user50 = new User(5, "孙五");
user10.next = user40;
user20.next = user30;
user30.next = user50;
MyLinkedList list = new MyLinkedList();
list.getHead().next = MyLinkedList.mergeList(user10,user20);
System.out.println("合并后的顺序");
list.showList();
}
}