数据结构之单链表的其他基本操作

文章结尾有单链表完整的代码

前面两篇文章介绍了一下单链表的增删改查,这篇文章来介绍一下单链表的一些其他基本操作。

1)统计单链表长度
2)获取单链表倒数第k个节点
3)单链表反转
4)单链表反向输出
5)两个有序单链表合并为一个单链表且仍然有序

根据前面两篇文章,应该都对单链表有一个简单的的认识,即想要操作单链表就需要一个节点指针,来指向单链表的每一个节点,要完成上面五个操作仍然需要一个节点指针,甚至还需要一个辅助指针。以下方法都不在单链表类内部,所以需要接收参数

统计单链表长度思路: 即计算单链表的节点个数,很明显需要遍历单链表,然后用一个计数器进行计数。

此时的节点指针初始化可以指向头节点,也可以指向单链表第一个节点,下面代码以指向单链表第一个节点为例。

参数:单链表的头节点(HeroNode)
返回值:单链表的节点长度(int)

public static int getSize(HeroNode head){
	//第一步,先判断单链表是否为空,如果为空就返回0
	if(head.getNext()==null){
		return 0;
	}
	//如果走到这一步,说明没有进入上面的if中,即链表不为空
	//定义一个节点指针,初始化指向第一个节点
	HeroNode cur = head.getNext();
	//定义一个计数器
	int size = 0;
	//开始遍历链表
	while(true){
		//因为节点指针指向的是单链表的节点
		//所以退出循环的条件是:cur指针当前指向的节点为空
		if(cur==null){
			break;
		}
		//当前cur指向的节点不为空,计数器就+1,指针往后移一位
		size++;
		cur = cur.getNext();
	}
	//循环结束之后,count就是单链表的大小,直接返回count
	return size;
}

在这里插入图片描述

获取单链表倒数第k个节点思路:
1)先判断传过来的k是否是一个有效参数,假设只有4个节点,但传过来一个5,或者传来一个负数,这样的参数都是无效的。判断参数是否有效要先知道单链表的长度,正好可以使用到上面的方法

2)已经知道单链表的长度,又知道是要倒数第k个节点,所以可以直接用for循环将一个节点指针cur直接指向倒数第k个节点,此时的节点指针初始化可以指向头节点,也可以指向单链表第一个节点,下面代码以指向单链表第一个节点为例。

3)cur指向单链表第一个节点,知道单链表长度size,获取倒数第k个节点只需要有for循环,循环size-k次,每次cur往后移一次

参数:单链表头节点(HeroNode),k(int)
返回值:单链表倒数第k个节点(HeroNode)

public static HeroNode getTheLastkNode(HeroNode head,int k){
	//先判断单链表是否为空,为空就返回null
	if(head.getNext()==null){
		return null;
	}
	//此处调用的是上面的方法,为的是获取单链表的长度
	int size = getSize(head);
	//检验参数是否有效,如果k为负数或大于单链表长度就返回null
	if(k<0 || k>size){
		return null;
	}
	//前面两个if都没进入,来到这里,说明参数和单链表都是有效的
	//创建一个节点指针,初始化指向单链表的第一个节点
	HeroNode cur = head.getNext();
	//因为cur指向的是单链表第一个节点,所以获取倒数第k个节点
	//只需要用for循环,循环size-k次
	//在每次循环都将节点指针往下一移一次
	for(int i=0;i < size-k;i++){
		cur = cur.getNext();
	}
	//循环之后的cur就指向了倒数第k个节点,直接返回
	return cur;
}

单链表反转思路:
单链表反转,不是输出时是反向输出,而是单链表整个结构都反过来(head节点除外),下面代码的cur节点指针指向单链表的第一个节点

1)创建一个reverseNode反转节点,不指向单链表任何一个节点

2)创建一个cur节点指针用来遍历单链表,将遍历的每个节点以头插法的形式插入到reverseNode反转节点后面,遍历完单链表之后,把reverseNode节点的next域赋值给head节点的next域,这样原来的单链表就完成了反转

3)ps:遍历完之后,reverseNode其实就可以作为一个单链表的节点,reverseNode后面的节点的排列顺序正好就是反转的单链表

参数:单链表的头节点(HeroNode)
返回值:无

public static void reverse(HeroNode head){
	//先判断单链表是否为空,为空就直接返回,不继续往下执行
	if(head.getNext()==null){
		System.out.println("单链表为空");
		return;
	}
	//如果没有进入if,就说明单链表不为空
	//创建一个reverseNode反转节点
	HeroNode reverseNode = new HeroNode(0,"","");
	//创建一个cur节点指针,并指向单链表的第一个节点
	HeroNode cur = head.getNext();
	//遍历时要操作cur所指向的节点
	//所以需要一个变量来保存这个节点
	HeroNode node;
	//遍历单链表
	while(true){
		//因为cur初始化指向的是单链表头第一个节点
		//所以退出循环的条件为cur为空
		if(cur==null){
			break;
		}
		//用node变量保存cur所指向的节点
		node = cur;
		//然后让cur节点指针往下移动
		cur = cur.getNext();
		//后面操作node节点,让其头插法插入reverseNode节点后面
		node.setNext(reverseNode.getNext());
		reverseNode.setNext(node);
		//因为cur指向的节点头插到reverseNode后
		//其next域不再指向单链表的第二个节点
		//所以需要node变量来保存cur指向的节点
	}
	//循环结束之后,reverseNode后面连着的节点就已经反转了
	//而此时的head节点的next域还是指向原单链表的第一个节点
	//而原单链表的第一个节点next域指向null
	//所以此时需要将reverseNode的next域赋值给head节点的next域
	head.setNext(reverseNode.getNext());
}

需要单链表反转时,调用如上方法,链表就反转了。
在这里插入图片描述

单链表反向输出思路:
1)最不济的方法就是用上面的单链表反转,然后遍历输出一下单链表,不过这个方法改变了单链表的结构不建议这样做。

2)依次遍历单链表,把单链表的每个节点压入栈或者是存放到一个数组,如果放入栈就遍历,放到一个数组就反向遍历数组
3)用栈的好处就是不需要先遍历单链表获得单链表的长度,也可以考虑使用ArrayList,下面代码使用栈,使用数组或ArrayList是类似的代码

参数:单链表头节点
返回值:无

//代码中用到了栈,所以需要导入栈的包
import java.util.Stack;
public static void reversePrint(HeroNode head){
	//先判断单链表是否为空,为空就直接返回,不往下执行
	if(head.getNext()==null){
		return;
	}
	//如果没有进入if就说明单链表不为空
	//创建一个cur节点指针用于遍历单链表
	//初始化指向单链表的第一个节点
	HeroNode cur = head.getNext();
	//创建一个栈,用户储存单链表的节点
	Stack<HeroNode> stack = new Stack<>();
	//开始遍历
	while(true){
		//因为cur指向的是单链表的第一个节点
		//所以退出循环条件是:cur指向的节点为null
		if(cur==null){
			break;
		}
		//如果cur指向的节点不为null,就加入栈内,然后cur往后移
		stack.push(cur);
		cur = cur.getNext();
	}
	//遍历完单链表后,栈内就有了单链表的所有元素
	//然后遍历栈,并输出,就完成了单链表的反向输出
	while(true){
		//当栈的大小为0时,说明遍历完栈,退出循环
		if(stack.size()==0){
			break;
		}
		//将栈顶元素取出并输出
		System.out.println(stack.pop());
	}
}

两个有序单链表合并为一个单链表且仍然有序思路:
1)如果你能写出按序号插入的方式,那么将两个有序单链表合并为一个单链表且仍然有序的操作就是非常简单的了。

2)通过上面的getSize方法分别获取两个单链表的大小,然后遍历较小的单链表,把遍历到的节点,挨个添加到另一个较大的单链表。因为是按序号插入的方式添加,所以最后这个较大的单链表最后合并了两个单链表并且仍然有序

参数:两个单链表(HeroNode)
返回值:合并好的单链表(HeroNode)
代码中的addByNo(HeroNode node);方法会放在后面贴出,前面文章也有。

public static SingleLinkedList mergeTwoSingleLinkedList(SingleLinkedList linkedList1,SingleLinkedList linkedList2){
    //先判断两个单链表哪个为空
    //如果有一个为空,那就直接返回另一个
    //如果两个都为空就随便返回一个
    HeroNode head1 = linkedList1.getHead();
    HeroNode head2 = linkedList2.getHead();
    if(head1.getNext()==null || head2.getNext()==null){
        return head1.getNext()==null ? linkedList2:linkedList1;
    }
    //调用上面的获取单链表长度的方法
    int size1 = getSize(head1);
    int size2 = getSize(head2);
    //需要一个临时变量来保存cur当前指向的节点
    //然后cur后移,再将node插入另一个单链表
    HeroNode node;
    //取较小的单链表进行遍历
    if(size1>size2){//如果size1大于size2,那么size2就是较小的
        //创建一个cur节点指针,初始化指向较小单链表的第一个节点
        HeroNode cur = head2.getNext();
        //遍历较小的单链表
        //然后用按序号插入法插入到较大的单链表
        while(true){
            if(cur==null){
                break;
            }
            node = cur;
            cur = cur.getNext();
            linkedList1.addByNo(node);

        }
        //循环结束,返回较大的单链表即为合并好且有序的单链表
        return linkedList1;
    }else{//反之就是size1较小
        //创建一个cur节点指针,初始化指向较小单链表的第一个节点
        HeroNode cur = head1.getNext();
        //遍历较小的单链表
        //然后用按序号插入法插入到较大的单链表
        while(true){
            if(cur==null){
                break;
            }
            node = cur;
            cur = cur.getNext();
            linkedList2.addByNo(node);
        }
        //循环结束,返回较大的单链表即为合并好且有序的单链表
        return linkedList2;
    }
}

addByNo:
此方法放在单链表类内部

public void addByNo(HeroNode node){
    HeroNode temp = head;
    Boolean flag = false;//标识编号是否已经存在
    while (true){//while循环结束,找到插入点
        if (temp.getNext()==null){
            break;
        }else if (temp.getNext().getNo() > node.getNo()){
            break;
        }else if (temp.getNext().getNo() == node.getNo()){
            flag = true;
        }
        temp = temp.getNext();
    }
    if (flag){
        System.out.printf("准备插入的节点编号%d已经存在\n",node.getNo());
    }else {
        node.setNext(temp.getNext());
        temp.setNext(node);
    }
}

最后附上单链表操作的全部代码和测试:
因为上传文件不知道哪里找,那就直接把全部代码粘贴上来。
SingleLinkedListDemo.java

package com.sixteen.linkedlist;

import java.util.Stack;

public class SingleLinkedListDemo {
    public static void main(String[] args) {
        SingleLinkedList linkedList = new SingleLinkedList();
        linkedList.addByNo(new HeroNode(2,"林冲2","豹子头2"));
        linkedList.addByNo(new HeroNode(4,"宋江4","及时雨4"));
        linkedList.addByNo(new HeroNode(6,"卢俊义6","玉麒麟6"));
        System.out.println("原链表------");
        linkedList.print();
        /*System.out.println("使用栈倒序输出");
        reversePrint2(linkedList.getHead());*/
        /*System.out.println("------------");
        System.out.println("链表节点="+getSize(linkedList.getHead()));
        System.out.println("倒数第4个节点"+getTheLastIndexNode(linkedList.getHead(),4));
        System.out.println("倒序输出------");
        reversePrint(linkedList.getHead());
        System.out.println("此时的链表-----");
        linkedList.print();*/
        /*reverse2(linkedList.getHead());
        System.out.println("链表反转后------");
        linkedList.print();*/
        /*SingleLinkedList linkedList1 = new SingleLinkedList();
        linkedList1.addByNo(new HeroNode(1,"林冲","豹子头"));
        linkedList1.addByNo(new HeroNode(3,"宋江","及时雨"));
        linkedList1.addByNo(new HeroNode(5,"卢俊义","玉麒麟"));
        SingleLinkedList linkedList2 = new SingleLinkedList();
        linkedList2.addByNo(new HeroNode(2,"林冲2","豹子头2"));
        linkedList2.addByNo(new HeroNode(4,"宋江4","及时雨4"));
        linkedList2.addByNo(new HeroNode(6,"卢俊义6","玉麒麟6"));
        SingleLinkedList singleLinkedList = mergeTwoSingleLinkedList(linkedList1, linkedList2);
        System.out.println("合并之后的链表-----");
        singleLinkedList.print();*/
    }

    //遍历的是链表1,所以建议链表1放短一点的链表

    /**
     * 因为这个单链表插入是根据序号排序插入,
     * 所以两个单链表合并,只需要遍历一个单链表的元素依次插入到另一个单链表
     * @param linkedList1
     * @param linkedList2
     * @return 合并之后的单链表
     */
    public static SingleLinkedList mergeTwoSingleLinkedList(SingleLinkedList linkedList1,
                                                            SingleLinkedList linkedList2){
        HeroNode head1 = linkedList1.getHead();
        HeroNode head2 = linkedList2.getHead();
        //链表1为空返回链表2,链表2为空返回链表1,都为空返回链表2
        if (head1.getNext()==null || head2.getNext()==null){
            return head1.getNext()==null ? linkedList2:linkedList1;
        }
        HeroNode temp = head1.getNext();//链表1的指针
        HeroNode node;
        //遍历链表2的节点,添加到链表1上
        /*
         * 此处node保存需要添加时的节点(即刚进入循环的temp)
         * 而temp需要指向链表1的下一个节点继续遍历链表1
         */
        while (temp!=null){
            node = temp;
            temp = temp.getNext();
            linkedList2.addByNo(node);
        }
        return linkedList2;
    }

    public static int getSize(HeroNode head){
        if (head.getNext()==null){
            return 0;
        }
        int size = 0;
        HeroNode temp = head.getNext();
        while (temp!=null){
            size++;
            temp = temp.getNext();
        }
        return size;
    }

    public static void reverse2(HeroNode head){
        //链表为空或者只有一个节点,无须反转
        if (head.getNext()==null || head.getNext().getNext()==null){
            return;
        }
        HeroNode temp = head.getNext();
        HeroNode reverseHead = new HeroNode(0,"","");
        HeroNode node;//用来过渡的一个节点
        while (temp!=null){
            node = temp;
            temp = temp.getNext();
            node.setNext(reverseHead.getNext());
            reverseHead.setNext(node);
        }
        head.setNext(reverseHead.getNext());
    }

    public static void reverse(HeroNode head){
        //链表为空或者只有一个节点,无须反转
        if (head.getNext()==null || head.getNext().getNext()==null){
            return;
        }
        int size = getSize(head);
        HeroNode[] nodes = new HeroNode[size];
        HeroNode temp = head.getNext();
        int count = 0;
        while (temp!=null){
            nodes[count] = temp;
            count++;
            temp = temp.getNext();
        }
        head.setNext(nodes[nodes.length-1]);//把头指向尾部节点
        nodes[0].setNext(null);
        for (int i = 1; i < nodes.length ; i++) {
            nodes[i].setNext(nodes[i-1]);
        }
    }

    public static void reversePrint2(HeroNode head){
        if (head.getNext()==null){
            return;
        }
        Stack<HeroNode> stack = new Stack<>();
        HeroNode temp = head.getNext();
        while (temp!=null){
            stack.push(temp);
            temp = temp.getNext();
        }
        while (stack.size() > 0){
            System.out.println(stack.pop());
        }

    }

    public static void reversePrint(HeroNode head){
        if (head.getNext()==null){
            return;
        }
        int size = getSize(head);
        HeroNode[] nodes = new HeroNode[size];
        HeroNode temp = head.getNext();
        int count = 0;
        while (temp!=null){
            nodes[count] = temp;
            count++;
            temp = temp.getNext();
        }
        for (int i = nodes.length-1; i > 0-1 ; i--) {
            System.out.println(nodes[i]);
        }
    }

    public static HeroNode getTheLastIndexNode(HeroNode head,int index){
        if (head.getNext()==null){
            return null;
        }
        //根据index找打倒数第index节点
        int size = getSize(head);//获取链表size
        if (index<=0 || index>size){//说明index不合法
            return null;
        }
        HeroNode temp = head.getNext();
        for (int i = 0; i < size-index; i++) {
            temp = temp.getNext();
        }
        return temp;
    }
}

class SingleLinkedList{
    private HeroNode head = new HeroNode(0,"","");

    public void setHead(HeroNode head) {
        this.head = head;
    }

    public HeroNode getHead() {
        return head;
    }

    public void add(HeroNode node){
        HeroNode temp = head;
        while (true){//while循环结束,temp指向最后一个节点
            if (temp.getNext()==null){
                break;
            }
            temp = temp.getNext();
        }
        temp.setNext(node);
    }
    public void addByNo(HeroNode node){
        HeroNode temp = head;
        Boolean flag = false;//标识编号是否已经存在
        while (true){//while循环结束,找到插入点
            if (temp.getNext()==null){
                break;
            }else if (temp.getNext().getNo() > node.getNo()){
                break;
            }else if (temp.getNext().getNo() == node.getNo()){
                flag = true;
            }
            temp = temp.getNext();
        }
        if (flag){
            System.out.printf("准备插入的节点编号%d已经存在\n",node.getNo());
        }else {
            node.setNext(temp.getNext());
            temp.setNext(node);
        }
    }
    public void updateByNo(HeroNode node){
        if (head.getNext()==null){
            System.out.println("链表为空");
            return;
        }
        HeroNode temp = head.getNext();
        Boolean flag = false;//标识是否有此编号的节点
        while (true){
            if (temp==null){
                break;
            }
            if (temp.getNo()==node.getNo()){
                flag = true;
                break;
            }
            temp = temp.getNext();
        }
        if (flag){
            temp.setName(node.getName());
            temp.setNickname(node.getNickname());
        }else System.out.printf("没有编号%d的节点\n",node.getNo());
    }
    public void removeByNo(int no){
        if (head.getNext()==null){
            System.out.println("链表为空");
            return;
        }
        HeroNode temp = head;
        boolean flag = false;//标识是否根据传来的no找到节点
        while (true){
            if (temp.getNext()==null){
                break;
            }else if (temp.getNext().getNo()==no){
                flag = true;
                break;
            }
            temp = temp.getNext();
        }
        if (flag){
            temp.setNext(temp.getNext().getNext());
        }else System.out.printf("没有编号%d的节点\n",no);

    }
    public void print(){
        if (head.getNext()==null){
            System.out.println("链表为空");
            return;
        }
        HeroNode temp = head.getNext();
        while (true){//while循环结束,temp指向null
            if (temp==null){
                break;
            }
            System.out.println(temp);
            temp = temp.getNext();
        }
    }
}

class HeroNode{
    private int no;
    private String name;
    private String nickname;
    private 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 + '\'' +
                '}';
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public HeroNode getNext() {
        return next;
    }

    public void setNext(HeroNode next) {
        this.next = next;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值