单链表的基础
遍历输出链表中的每一个节点
思路:直接对链表进行遍历,辅助指针每移动一个节点,就将该节点的数据输出出来,然后辅助指针指向下一个节点。
public void show() {
//先判断是否为空的链表
if (head.next == null) {
System.out.println("链表为空");
return;
}
//需要借助一个辅助节点来遍历整个链表
LinkedNode1 temp = head.next;
while (true) {
//如果已经遍历到最后一个节点之后,就可以跳出循环
if (temp == null) {
break;
}
System.out.println(temp);
//输出后要将temp后移,找下一个节点,要不然会进入死循环
temp = temp.next;
}
}
查找单链表中节点的个数
思路:直接对链表进行遍历,辅助指针每移动一个节点,count就+1,然后指向下一个节点,最后返回count就是节点数目
public int countNode(){
LinkedNode1 temp=head;
int count=0;
while(true){
if(temp.next==null){
break;
}
count++;
temp=temp.next;
}
return count;
}
输出倒数第k个节点
思路:
- 先遍历出整个链表中有多少的节点sum
- 然后倒数第k个节点就是正数第index=sum-k+1个节点
- 设置一个计数变量count,在遍历时进行count++
- 只要在此遍历时有index==count,故说明此节点就是我们要找的倒数第k个节点,输出此节点即可
public void returnK(int k){
int sum=countNode();//调用了计数的方法来求出总的节点数
int index=sum-k+1;
LinkedNode1 temp=head;
int count=0;
if(k>sum||k<=0){
System.out.println("链表中没有这么多的数或者请输入大于0的值");
return;
}
while(true){
if(count++ == index){
System.out.println(temp);
break;
}
temp=temp.next;
}
}
对单向链表进行反转
如图所示。至于为什么要引入temp节点,主要是因为在将原始链表中的节点移动到新的链表后,原始链表将会失去这个节点,而指向这个节点的temp也被移走了,因此要引入cur节点来指向原始链表的下一个节点,进行遍历。
//链表的头尾反转
/*思路:1重新创建一个头节点reverseHead
* 2遍历原始的链表,每遍历一个就把这个元素取出,放到reverseHead的最前面
* 3最后将原始链表的头节点指向reverseHead的next节点
* */
public void reverse(LinkedNode1 head){
//创建一个新的节点
LinkedNode1 reverseHead=new LinkedNode1(0,"","");
LinkedNode1 temp=head.next;
LinkedNode1 cur=null;
while(temp!=null){
//因为temp在取出元素时就不能指向下一个节点,故需要领取辅助节点来记住temp.next的位置
cur=temp.next;
//只要原始链表中仍然有元素,就把当前元素放到reverseHead的下一个节点
//第一步:把temp所指的当前元素指向reverseHead的后面那个节点。
temp.next=reverseHead.next;
//第二步,将reverseHead指向temp节点
reverseHead.next=temp;
//利用cur辅助节点来对原始链表上进行遍历
temp=cur;
}
//最后一步,将原始链表的头节点指向新链表头节点的下一个节点。
head.next=reverseHead.next;
//调用一下方法直接显示一下
}
对单链表中的数据进行逆序打印
思路
- 先将链表反转,在正着遍历输出。但是会破坏原始链表!不推荐
- 利用栈结构的特点,先进后出。只要按顺序将链表进行入栈操作,然后再将栈中的数据进行弹栈,就可以实现逆序打印
public void reversePrint(LinkedNode1 head){
//创建辅助指针来遍历链表
LinkedNode1 temp=head.next;
if(temp==null){
System.out.println("链表为空!");
return;
}
//新建一个栈
Stack<LinkedNode1> stack = new Stack<>();
while(temp!=null){
//只要链表中有元素,就往栈中压栈
stack.push(temp);
temp=temp.next;
}
while(!stack.empty()){
//只要栈中还有元素就给我弹栈!
System.out.println(stack.pop());
}
}
合并两个有序的单链表,合并之后仍然有序。
基本思路:
从图中可以看出,第一次合并时,只需要先判断出那个节点的数更小,确定要连接的节点之后,
- temp.next=cur1即:先指向此节点
- temp=temp.next; 让合并之后的辅助指针temp指向cur1
- cur1=cur1.next让cur1节点后移
当连到第二个链表时, - temp.next=cur2即:先指向此节点
- temp=temp.next; 让合并之后的辅助指针temp指向cur2
- cur2=cur2.next让cur2节点后移
以此类推:当两个链表长度不相同时,先比较相同的部分,剩下多的那个,肯定是比前面的都大,不需要比较了,直接加在新链表的后面即可,让temp指针指向即可。
public LinkedNode1 combine(LinkedNode1 node1, LinkedNode1 node2) {
LinkedNode1 temp = head;
LinkedNode1 cur1 = node1.next;
LinkedNode1 cur2 = node2.next;
while (cur1 != null && cur2 != null) {
if (cur1.no <= cur2.no) {
temp.next = cur1;
temp = temp.next;
cur1 = cur1.next;
} else {
temp.next = cur2;
temp = temp.next;
cur2 = cur2.next;
}
}
temp.next = cur1 == null ? cur2 : cur1;
return head.next;
}
二刷心得:
算法真的是需要多次练习,只要理解了,然后在量上慢慢积累,就可以很快写出来
package com.njupt.List.Test;
import java.util.Stack;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/12/23/20:41
* @Description:面试必会的几个关于链表的笔试题目3
*/
public class ListHomeWork {
public static void main(String[] args) {
/* ListOO list = new ListOO();
for(int i=1;i<=10;i++){
NodeOO nodeOO = new NodeOO(i, i + "号节点");
list.add(nodeOO);
}
list.show();
System.out.println();
System.out.println("倒数第2个节点");
list.countFromEnd(2);
ListOO list2=list.reverse(list);
System.out.println();
System.out.println("=========实现逆序打印========");
ListOO listBack = new ListOO();
for(int i=1;i<=10;i++){
NodeOO nodeOO = new NodeOO(i, i + "号节点");
listBack.add(nodeOO);
}
listBack.reversePrint(listBack);
System.out.println();
System.out.println("=====打印之后,并没有改变原来的链表顺序====");
listBack.show();*/
ListOO list1 = new ListOO();
for(int i=1;i<=11;i=i+2){
NodeOO nodeOO = new NodeOO(i, i + "号节点");
list1.add(nodeOO);
}
list1.show();
ListOO list2 = new ListOO();
for(int i=2;i<=18;i=i+2){
NodeOO nodeOO = new NodeOO(i, i + "号节点");
list2.add(nodeOO);
}
list2.show();
System.out.println("开始测试两个链表融合");
ListOO combine = combine(list1, list2);
combine.show();
}
public static ListOO combine(ListOO list1,ListOO list2){
NodeOO temp1=list1.head;
NodeOO temp2=list2.head;
ListOO list = new ListOO();
NodeOO newHead=list.head;
NodeOO temp=newHead;
NodeOO cur1=temp1.next;
NodeOO cur2=temp2.next;
while (cur1!=null&&cur2!=null){
temp1=cur1;
temp2=cur2;
if(temp1.id<temp2.id){
temp.next=temp1;
temp=temp.next;
cur1=cur1.next;
}else {
temp.next=temp2;
temp=temp.next;
cur2=cur2.next;
}
}
if(temp1.next==null){
temp.next=temp2;
}else {
temp.next=temp1;
}
return list;
}
//
}
class ListOO{
public NodeOO head=new NodeOO(0,"");
//往链中添加数据
public void add(NodeOO node){
if (head.next==null){
head.next=node;
System.out.println(node+"添加完毕!");
return;
}
NodeOO temp=head;
while (temp.next!=null){
if(temp.next.id<node.id){
temp=temp.next;
}else {
//完成添加操作
node.next=temp.next;
temp.next=node;
System.out.println(node+"添加完毕!");
return;
}
}
//完成添加操作
temp.next=node;
System.out.println(node+"添加完毕!");
}
//遍历链表中的所有数据并且返回链表中节点的个数
//记录链表的长度
public int show(){
int count=0;
if(head.next==null){
System.out.println("当前链表为空!节点数为0");
return count;
}
NodeOO temp=head;
while (temp.next!=null){
temp=temp.next;
count++;
System.out.print(temp+"-->");
}
System.out.println("共有节点数为:"+count);
return count;
}
//输出倒数第2个节点
public void countFromEnd(int num){
if(show()<num){
System.out.println("当前节点查找数错误,链表中只有"+show()+"个节点!");
return;
}
NodeOO slow=head;
NodeOO fast=head;
for(int i=0;i<num;i++){
fast=fast.next;
}
while (fast.next!=null){
slow=slow.next;
fast=fast.next;
}
System.out.println("倒数第个"+num+"节点是:"+slow.next);
}
//对单向链表进行反转
public ListOO reverse(ListOO listOO){
System.out.println("反转之前:");
int i=listOO.show();
if(i==0){
System.out.println("当前链表为空,不能反转");
return null;
}else if(i==1){
System.out.println(listOO.head.next);
return listOO;
}else {
NodeOO temp=listOO.head.next;
NodeOO newHead=new NodeOO(0,"");
while (temp!=null){
//先让辅助指针向后移动一个
NodeOO cur=temp.next;
temp.next=newHead.next;
newHead.next=temp;
//在把辅助指针赋值为temp
temp=cur;
}
//到此处已经反转成功了,此时需要将head头节点换回来
listOO.head=newHead;
System.out.println("反转之后的链表为:");
listOO.show();
return listOO;
}
}
//对单向链表的数据进行逆序打印:思路是为了不改变链表的结构,需要借助stack栈来实现先进后出
public void reversePrint(ListOO listOO){
int length = listOO.show();
if(length==0){
return;
}else if(length==1){
System.out.println(listOO.head.next);
return;
}
Stack<NodeOO> stack = new Stack<>();
NodeOO temp=listOO.head.next;
while (temp!=null){
stack.push(temp);
temp=temp.next;
}
System.out.println("逆序打印的结果为:");
while (!stack.isEmpty()){
System.out.print(stack.pop()+"-->");
}
}
}
class NodeOO{
public int id;
public String name;
public NodeOO next;
public NodeOO(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "NodeOO{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}