数据结构(一)——链表的基本操作

关于数据结构,这次总算下定决心进行一个总结了,之前各种零星散散的总结,并不成体系,这里对一些基本的数据结构进行一个总结。大学学过数据结构的理论,但是一直没用代码完全实现过。

链表和结点的定义

节点的定义

/**
 * autor:liman
 * comment: 链表对应的节点
 */
public class ListNode {

    public int value;
    public ListNode next;

    public ListNode(int value) {
        this.value = value;
    }
}

这个很简单的定义。没啥可说的,学过数据结构这个都很简单

链表的定义

链表的定义就是在节点的基础上,封装一些基础操作,一些插入删除的操作百度就好,这里直接贴出代码

package com.learn.LinkList;

/**
 * autor:liman
 * comment:
 */
public class SelfLinkedList {

    /**
     * 头结点的插入
     * @param head
     * @param newHead
     */
    public static void headInsert(ListNode head,ListNode newHead){
        ListNode old = head;
        head = newHead;
        head.next = old;
    }

    /**
     * 未节点的插入
     * @param tail
     * @param newTail
     */
    public static void tailInsert(ListNode tail,ListNode newTail){
        ListNode old = tail;
        tail = newTail;
        newTail.next = null;
        old.next = tail;
    }

    /**
     * 遍历链表
     * @param head
     */
    public static void traverse(ListNode head){
        while(head!=null){
            System.out.print(head.value+" ");
            head = head.next;
        }
        System.out.println();
    }

    /**
     * 查找节点,返回节点索引
     * @param head
     * @return
     */
    public static int findNode(ListNode head,int value){
        int index = -1;
        int count = 0;
        while(head!=null){
            if(head.value == value){
                index = count;
                return index;
            }
            count++;
            head = head.next;
        }
        return index;
    }

    /**
     * 在pre节点的后面插入s节点
     * @param pre
     * @param s
     */
    public static void insertAfterNode(ListNode pre,ListNode s){
        ListNode pAfter = pre.next;
        pre.next = s;
        s.next = pAfter;
    }

    /**
     * 删除节点s,将s的next复制到s,然后删除s的后继节点
     * @param head
     * @param s
     */
    public static void deleteNode(ListNode head,ListNode s){
        if(s!=null && s.next!=null){//这里不包含删除尾节点的情况
            ListNode sNext = s.next;
            s.value = sNext.value;
            //删除s的下一个节点
            s.next = sNext.next;
            sNext=null;
        }

        //如果是删除尾节点
        if(s.next==null){
            //遍历找打前驱
            while(head!=null){
                if(head.next!=null && head.next == s){
                    head.next=null;
                    break;
                }
                head=head.next;
            }
        }
    }

}

其中删除节点的操作,找到前驱节点比较麻烦,这里就偷了个懒,直接将后继节点复制到当前节点,然后删除当前节点。

一些实战操作

链表翻转

思路:利用三个指针依次走链,每走一步将队伍后面两个指针翻转即可。

    /**
     * 翻转链表,时间复杂度O(n),空间复杂度O(1)
     *
     * @param head
     */
    public static ListNode reverseList(ListNode head) {
        ListNode preNode = null;
        ListNode afterNode = null;
        while (head != null){
            afterNode = head.next;
            head.next=preNode;
            preNode = head;
            head = afterNode;
        }
        return preNode;
    }

获取中间节点

思路:取中间节点,如果链表长度是奇数,则直接取中间的。如果是偶数则取中间的前一个。

定义两个指针,快指针每次走两步,慢指针每次走一步,当快指针走到尾的时候,慢指针则为中间节点。

    /*
     * @param head
     * @return
     */
    public static ListNode getMiddleNode(ListNode head){
        if(head==null){
            return head;
        }
        ListNode fastNode = head;
        ListNode slowNode = head;
        while(fastNode.next!=null && fastNode.next.next!=null){
            slowNode = slowNode.next;
            fastNode = fastNode.next.next;
        }
        return slowNode;
    }

合并两个有序链表

这个有几种实现方式,一种是递归,一种是非递归。

递归实现

    /**
     * 递归的方式合并有序链表
     *
     * @param head01
     * @param head02
     * @return
     */
    public static ListNode mergeTwoListRecursive(ListNode head01, ListNode head02) {
        //递归出口
        if (head01 == null && head02 == null) {
            return null;
        }
        if (head01 == null) {
            return head02;
        }
        if (head02 == null) {
            return head01;
        }

        ListNode head = null;//合并后的头节点
        if (head01.value > head02.value) {
            head = head02;
            head.next = mergeTwoListRecursive(head01, head02.next);
        } else {
            head = head01;
            head.next = mergeTwoListRecursive(head01.next, head02);
        }
        return head;
    }

非递归实现

    /**
     * 非递归合并两个链表。
     *
     * @param head01
     * @param head02
     * @return
     */
    public static ListNode mergeTwoList(ListNode head01, ListNode head02) {
        if (head01 == null || head02 == null) {
            return head01 != null ? head01 : head02;
        }

        ListNode head = head01.value <= head02.value ? head01 : head02;
        ListNode cur1 = head == head01 ? head01 : head02;
        ListNode cur2 = head == head01 ? head02 : head01;

        ListNode pre = null;
        ListNode next = null;
        while (cur1 != null && cur2 != null) {
            if (cur1.value <= cur2.value) {//将cur1合并到目标链表
                pre = cur1;
                cur1 = cur1.next;
            } else {//将cur2合并到目标链表
                next = cur2.next;
                pre.next = cur2;
                cur2.next = cur1;
                pre = cur2;
                cur2 = next;
            }
        }
        pre.next = cur1 == null ? cur2 : cur1;
        return head;
    }

非递归的另一种实现,我个人觉得这种思路要稍微简单点

    /**
     * 合并链表,自己的思想,和当时的数据结构书籍上的处理一样
     *
     * @param head01
     * @param head02
     * @return
     */
    public static ListNode mergeTwoListSelf(ListNode head01, ListNode head02) {
        if (head01 == null || head02 == null) {
            return head01 != null ? head01 : head02;
        }
        ListNode pNode = new ListNode(-65535);//建立一个节点用于头节点
        ListNode head = head01.value <= head02.value ? head01 : head02;//构建head,后面的构建操作,交给了pNode
        ListNode cur01 = head01;
        ListNode cur02 = head02;
        while (cur01 != null && cur02 != null) {
            if (cur01.value <= cur02.value) {
                pNode.next = cur01;
                cur01 = cur01.next;
            } else {
                pNode.next = cur02;
                cur02 = cur02.next;
            }
            pNode = pNode.next;
        }

        pNode.next = cur01 == null ? cur02 : cur01;
        return head;
    }

一些面试题

奇数升序,偶数降序

一个链表,奇数位按升序,偶数位按降序排列,对该链表进行排序。例如:1->8->3->6->5->4->7->2->9,需要对其排序

思路:1、按照奇数位和偶数位进行拆分,拆分成两个链表。2、对偶数位出来的链表进行翻转。3、合并排序

这里直接贴出完整的实现

/**
 * autor:liman
 * createtime:2020/2/4
 * 一个链表,奇数位升序,偶数位降序,对该链表进行排序
 */
public class InterviewTitleOne {

    /**
     * 分成三步:
     * 1、按照奇数位和偶数位进行拆分
     * 2、对偶数位进行翻转
     * 3、合并排序
     */
    public static void main(String[] args) {
        ListNode node01 = new ListNode(1);
        ListNode node02 = new ListNode(8);
        ListNode node03 = new ListNode(3);
        ListNode node04 = new ListNode(6);
        ListNode node05 = new ListNode(5);
        ListNode node06 = new ListNode(4);
        ListNode node07 = new ListNode(7);
        ListNode node08 = new ListNode(2);
        ListNode node09 = new ListNode(9);

        node01.next = node02;
        node02.next = node03;
        node03.next = node04;
        node04.next = node05;
        node05.next = node06;
        node06.next = node07;
        node07.next = node08;
        node08.next = node09;

        ListNode[] listNodes = getList(node01);
        ListNode head01 = listNodes[0];
        ListNode head02 = listNodes[1];
        //翻转偶数位的链表
        head02=reverseList(head02);

        ListNode result = mergetList(head01,head02);
        SelfLinkedList.traverse(result);

    }

    /**
     * 拆分链表,按奇偶进行拆分
     *
     * @param head
     * @return
     */
    public static ListNode[] getList(ListNode head) {
        ListNode head01 = null;
        ListNode head02 = null;

        ListNode cur01 = null;
        ListNode cur02 = null;
        int count = 1;
        while (head != null) {
            if (count % 2 == 1) {//奇数节点
                if(cur01!=null){
                    cur01.next = head;
                    cur01 = cur01.next;
                }else{
                    cur01 = head;
                    head01 = cur01;
                }
            }else{
                if(cur02!=null){
                    cur02.next = head;
                    cur02 = cur02.next;
                }else{
                    cur02 = head;
                    head02 = cur02;
                }
            }
            head = head.next;
            count++;
        }
        cur01.next = null;
        cur02.next = null;
        ListNode[] nodes = new ListNode[]{head01,head02};
        return nodes;
    }

    /**
     * 翻转链表
     * @param head
     * @return
     */
    public static ListNode reverseList(ListNode head){
        ListNode pre = null;
        ListNode next = null;
        while(head!=null){
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }

    /**
     * 合并两个链表(递归实现)
     * @param head01
     * @param head02
     * @return
     */
    public static ListNode mergetList(ListNode head01,ListNode head02){
        if(head01 == null && head02 ==null){
            return null;
        }
        if(head01==null){
            return head02;
        }
        if(head02 == null){
            return head01;
        }
        ListNode head = null;
        if(head01.value>head02.value){
            head=head02;
            head.next = mergetList(head01,head02.next);
        }else{
            head = head01;
            head.next = mergetList(head01.next,head02);
        }
        return head;
    }

}

其中的翻转和合并操作之前已经介绍过,这里不再赘述,这里值得关注的是拆分操作。

实现链表的归并排序

归并排序应该算是链表排序最佳的选择了,保证了最好和最好的时间复杂度都是nlogn,而且归并排序在数组中空间复杂度为O(n),在链表中也变成了O(1)。

思路:1、将待排序的数组(链表)一分为二。2、递归的将左边部分进行归并排序。3、递归的将右边部分进行归并排序。4、将两个部分进行合并排序,得到结果。

package com.learn.LinkList;

/**
 * autor:liman
 * createtime:2020/2/4
 */
public class InterviewTitleTwo {

    public static void main(String[] args) {
        ListNode node01 = new ListNode(1);
        ListNode node02 = new ListNode(8);
        ListNode node03 = new ListNode(3);
        ListNode node04 = new ListNode(6);
        ListNode node05 = new ListNode(5);
        ListNode node06 = new ListNode(4);
        ListNode node07 = new ListNode(7);
        ListNode node08 = new ListNode(2);
        ListNode node09 = new ListNode(9);

        node01.next = node02;
        node02.next = node03;
        node03.next = node04;
        node04.next = node05;
        node05.next = node06;
        node06.next = node07;
        node07.next = node08;
        node08.next = node09;

        ListNode sortedList = sortList(node01);
        SelfLinkedList.traverse(sortedList);
    }

    /**
     * 链表的归并排序算法
     *
     * @param head
     * @return
     */
    public static ListNode sortList(ListNode head) {
        if (head == null || head.next==null) {//0个或者1个元素,不用排序,直接返回
            return head;
        }
        ListNode middleNode = getMiddleNode(head);
        ListNode rightFirst = middleNode.next;
        middleNode.next=null;//拆成两个链表;
        ListNode node=merge(sortList(head),sortList(rightFirst));
        return node;
    }

    /**
     * 获取中间节点
     *
     * @param head
     * @return
     */
    public static ListNode getMiddleNode(ListNode head) {
        if (head == null) {
            return head;
        }
        ListNode fastNode = head;
        ListNode slowNode = head;
        while (fastNode.next != null && fastNode.next.next != null) {
            slowNode = slowNode.next;
            fastNode = fastNode.next.next;
        }
        return slowNode;
    }

    /**
     * 非递归有序合并两个链表
     *
     * @param head01
     * @param head02
     * @return
     */
    public static ListNode merge(ListNode head01, ListNode head02) {
        if (head01 == null || head02 == null) {
            return head01 != null ? head01 : head02;
        }

        ListNode head = head01.value <= head02.value ? head01 : head02;
        ListNode cur1 = head == head01 ? head01 : head02;
        ListNode cur2 = head == head01 ? head02 : head01;

        ListNode pre = null;
        ListNode next = null;
        while (cur1 != null && cur2 != null) {
            if (cur1.value <= cur2.value) {
                pre = cur1;
                cur1 = cur1.next;
            } else {
                next = cur2.next;
                pre.next = cur2;
                cur2.next = cur1;
                pre = cur2;
                cur2 = next;
            }
        }
        pre.next = cur1 == null ? cur2 : cur1;
        return head;
    }
}

总结:

一些基础操作的总结。

发布了129 篇原创文章 · 获赞 37 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/liman65727/article/details/104174633