zoukankan      html  css  js  c++  java
  • 链表的经典习题

    练习1•:链式栈

    //链式栈(top指向第一位无效的头结点)
    public class LinkedStack<E> {
        Node<E> top;
        //内部类
        class Node<E> {
            protected E data;
            protected Node<E> next;
    
            public Node(E data) {
                this.data = data;
            }
        }
        public LinkedStack() {
            top = new Node(new Object());
        }
        //头插法
        public void push(E val){
            Node<E> newNode=new Node(val) ;//创建一个值为val的节点
            newNode.next=top.next; //新插入节点的next指向原top指向的next
            top.next=newNode;//再把top.next指向新节点
        }
        //获取栈顶元素并删除
        public E remove(){
            if(top.next==null){
                throw new UnsupportedOperationException("the stack has been empty");
            }
            E result=top.next.data;
            top.next=top.next.next;
            return result;
        }
        public E peek(){
            if(top.next==null){
                throw new UnsupportedOperationException("the stack has been empty");
            }
            return top.next.data;
        }
    
        //从top节点的下一个开始遍历,不为空则一直的打印
        public void show(){
            Node<E> tmp=top.next;
            while (tmp!=null){
                System.out.print(tmp.data+" ");
                tmp=tmp.next;
            }
            System.out.println();
        }
    
        public static void main(String[] args) {
            LinkedStack<Integer> l=new LinkedStack();
            l.push(3);
            l.push(4);
            l.push(5);
            l.show();
            System.out.println(l.peek());
    
            l.remove();//删除5
            l.show();
            l.remove();//删除4
            l.show();
            l.remove();//删除3
            l.show();
        }
    
    }

     

    练习2:查找链表中倒数第k个节点

    //单链表查找倒数第k个节点
     class FindLastK<E> {
        Node<E> head;
    
        class Node<E> {
            protected E data;
            protected Node<E> next;
    
            public Node(E data,Node<E> next) {
                this.data = data;
                this.next=next;
            }
        }
    //    //构造函数,第一个头结点有效,所以不需要构造函数
    //    public FindLastK() {
    //        this.head = new Node(new Object(),null);
    //    }
    
        //找倒数第k个数的方法
        public E lastK(int k){
            Node<E> cur1=this.head.next;
            Node<E> cur2=this.head;//cur2指向头
            if(head==null){
                return null;
            }
    
            if(k>getLenth()||k<=0){
                return null;
            }
            else if(k==getLenth()){
                return head.data;
            }
            for(int i=1;i<=k;i++) {
                cur2=cur2.next;
                if(cur2==null){
                    return null;
                }
            }
            while (cur2.next!=null){
                cur2=cur2.next;
                cur1=cur1.next;
            }
            return cur1.data;
        }
        //获取链表长度
        public int getLenth(){
            int length=0;
            Node<E> cu=head;
            if(head==null){
                return 0;
            }
            while (cu!=null){  //应该让cur去遍历,不能让head直接遍历,否则打印一次后show再次打印链表就会空
                length++;
                cu=cu.next;
            }
            return length;
        }
    
        //尾插法
        public void add(E val) {
            Node<E> newNode = new Node(val,null);
            Node<E> current = head;
            if(head==null){
                head=newNode;
                return;
            }
            while (current.next != null) {
                current = current.next;
            }
            current.next = newNode;
           // newNode.next=null;
        }
    
        public void show() {
            Node<E> current = head;
            if(current==null){
                System.out.println("链表空!!");
                return;
            }
            while (current!=null&&current.next!= null) {
                System.out.print(current.data + " ");
                current = current.next;
            }
            System.out.println(current.data);
        }
    
        }
    
    public class FindLastKTest{
        public static void main(String[] args) {
            FindLastK<Integer> f=new FindLastK<>();
            f.add(3);
            f.add(4);
            f.add(5);
            f.add(6);
            f.show();
            System.out.println("该链表的长度:"+f.getLenth());
    
            System.out.println(f.lastK(1));//6
            System.out.println(f.lastK(4));//3
            System.out.println(f.lastK(5));//null
            f.show();
    
            }
        }

     练习3:找到带环链表的入口节点

    import sun.awt.image.ImageWatched;
    
    //单链表查找倒数第k个节点
    public class LinkedExercise<E> {
        Node<E> head;
    
        static class Node<E> {
            protected E data;
            public Node<E> next;
    
            public Node(E data, Node<E> next) {
                this.data = data;
                this.next = next;
            }
        }
    //    //构造函数,第一个头结点有效,所以不需要构造函数
    //    public FindLastK() {
    //        this.head = new Node(new Object(),null);
    //    }
    
        //找倒数第k个数的方法
        public E lastK(int k) {
            Node<E> cur1 = this.head.next;
            Node<E> cur2 = this.head;//cur2指向头
            if (head == null) {
                return null;
            } else if (k > getLenth() || k <= 0) {
                return null;
            }
            //如果找的倒数第k个恰好为链表长度,直接将头结点的数返回
            else if (k == getLenth()) {
                return head.data;
            }
            for (int i = 1; i <= k; i++) {
                cur2 = cur2.next;
                if (cur2 == null) {
                    return null;
                }
            }
            //两个节点同时遍历,快节点遍历到最后一个节点时,慢节点指向的节点就是要找的节点
            while (cur2.next != null) {
                cur2 = cur2.next;
                cur1 = cur1.next;
            }
            return cur1.data;
        }
    
        //获取链表长度
        public int getLenth() {
            int length = 0;
            Node<E> cu = head;
            if (head == null) {
                return 0;
            }
            while (cu != null) {  //应该让cur去遍历,不能让head直接遍历,否则打印一次后show再次打印链表就会空
                length++;
                cu = cu.next;
            }
            return length;
        }
        //判断单链表是否有环
    
        /**
         * 快慢指针,先通过两个指针找到环内的节点,然后再一个节点从相交节点出发,
         * 另一个节点从头结点出发,再次相交的节点就是环的入口节点
         *
         * @return
         */
        public E getLinkCirclrVal() {
            Node<E> slow = this.head;
            Node<E> fast = this.head;
            //找到了相交节点
            while (fast != null && fast.next != null) {
                slow = slow.next;
                fast = fast.next.next;
                if (slow == fast) {
                    break;
                }
            }
            if (fast == null) {
                return null;
            } else {
                fast = this.head;
                while (fast != slow) {
                    fast = fast.next;
                    slow = slow.next;
                }
                return slow.data;
            }
        }
    
    
        //尾插法
        public void add(E val) {
            Node<E> newNode = new Node(val, null);
            Node<E> current = head;
            if (head == null) {
                head = newNode;
                return;
            }
            while (current.next != null) {
                current = current.next;
            }
            current.next = newNode;
            // newNode.next=null;
        }
    
        public void show() {
            Node<E> current = head;
            if (current == null) {
                System.out.println("链表空!!");
                return;
            }
            while (current != null && current.next != null) {
                System.out.print(current.data + " ");
                current = current.next;
            }
            System.out.println(current.data);
        }
        //构造带环的链表
        public void con(LinkedExercise<E> link){
            //将两个节点都指向头
            LinkedExercise.Node list=link.head;
            LinkedExercise.Node p=link.head;
            //list遍历到最后一个节点
            while (list.next!=null){
                list=list.next;
            }
            //让最后一个节点的写一个指向头结点的下一个 6指向5
            list.next=p.next.next;
        }
        public static void main(String[] args) {
            LinkedExercise<Integer> f = new LinkedExercise<>();
            f.add(3);
            f.add(4);
            f.add(5);
            f.add(6);
            f.show();
            System.out.println("该链表的长度:" + f.getLenth());
    
            System.out.println(f.lastK(1));//6
            System.out.println(f.lastK(4));//3
            System.out.println(f.lastK(5));//null
    
            f.con(f);
            System.out.println("环的入口节点:"+f.getLinkCirclrVal());
    
        }
    }

     练习4:合并两个有序的链表(头结点无效时)

    包含头结点无效的大多数函数:

    class SingleLinekdListTakeHead<E extends Comparable> {
        protected Node<E> head;//头节点
    
        class Node<E> {
            protected E data;//数据域
            protected Node<E> next;//next引用域
    
            public Node(E data, Node<E> next) {
                this.data = data;
                this.next = next;
            }
        }
    
        //初始化head
        public SingleLinekdListTakeHead() {
            head = new Node(new Object(), null);
        }
    
        //在head之后直接插入一个节点,头插法
        public void addHead(E element) {
            Node<E> newNode = new Node(element, null);
            newNode.next = head.next;//先让新添加的节点的下一个指向原head节点指向的
            head.next = newNode;//再让head节点指向新节点
        }
    
        //尾插法
        public void addTail(E element) {
            Node<E> newNode = new Node(element, null);
            Node<E> tail = head;//定义一个节点从头走到尾
            //tail走到当前链表的尾部
            while (tail.next != null) {
                tail = tail.next;
            }
            tail.next = newNode;
            newNode.next=null;
        }
    
        /**
         * 固定位置插入一个节点
         * 判断参数合法性
         * 找到pos位置的前一个节点
         * @param pos  固定位置
         * @param element  元素
         */
        public void addPos(int pos, E element) {
            if (pos <= 0 || pos > getLength()) {
                return;
            }
            Node<E> prev = head.next;
            int index = 1;
    
            while (index++ < pos - 1) {
                prev = prev.next;
            }
            Node<E> newNode = new Node<>(element, null);
            newNode.next = prev.next;
            prev.next = newNode;
        }
        //删除元素为element的节点
        public boolean remove(E element) {
            //如果只有一个头结点,返回false
            if (head.next == null) {
                return false;
            }
            //找到该元素所对应的节点 + 该元素所对应的节点的前一个
            //从头结点开始遍历
            Node<E> tmp = head;
    
            while (tmp != null) {
                if (tmp.next != null && tmp.next.data == element) {
                    //tmp.next是我们要删除的节点 tmp是删除节点的前一个
                    tmp.next = tmp.next.next;
                    return true;
                }
                tmp = tmp.next;
            }
            return false;
        }
      //设置某个位置的值为newElement
        public void set(int pos, E newElement){
            if(pos <= 0 || pos > getLength()){
                return;
            }
            //找pos位置的节点
            Node<E> tmp = head.next;
            for(int i=1; i < pos; i++){
                tmp = tmp.next;
            }
            tmp.data = newElement;
        }
         //得到某个元素的值
        public E get(E element){
            Node<E> tmp = head.next;//从有效节点开始遍历
    
            while(tmp != null){
                if(tmp.data == element){
                    return tmp.data; //找到的话,返回该节点
                }
                tmp = tmp.next;
            }
            return null;
        }  //合并两个有序的单链表
        public void merge(SingleLinekdListTakeHead<E> list2){
    //        LinkedExercise<E> list3=new LinkedExercise<>();
            Node<E> p=this.head;//最后合并成功的的链表
            Node<E> p1=this.head.next;//第一的链表
            Node<E> p2=list2.head.next;//第二个链表
    
            while (p1!=null && p2!=null){
                if(p1.data.compareTo(p2.data)>=0){
                    p.next=p2;
                    //list3.add(p2.data);
                    p2=p2.next;
                }else {
                    p.next=p1;
                    // list3.add(p1.data);
                    p1=p1.next;
                }
                p=p.next;
            }
            if(p1!=null){ //链表1还有剩余节点
                p.next=p1;
            }
            if(p2!=null){
                p.next=p2;
            }
          //  return p.data;
    
        }
        //返回长度
        public int getLength() {
            Node<E> tmp = head.next;
            int length = 0;
            while (tmp != null) {
                length++;
                tmp = tmp.next;
            }
            return length;
        }
         //打印栈
        public String toString() {
            StringBuilder strs = new StringBuilder();
    
            Node<E> tmp = head.next;
            while (tmp != null) {
                strs.append(tmp.data + " ");
                tmp = tmp.next;
            }
            return strs.toString(); //strs是StringBuilder类型,应该添加toString方法,才能返回String类型的
        }
    //逆置带有头结点的单链表 public void reverse(){ if(head.next==null||head.next.next==null){ return; }else { Node<E> cur=this.head.next.next;//指向第二个有效的节点 this.head.next.next=null; Node<E> pos=null; while (cur!=null){ pos=cur.next;//先将cur.next指向pos cur.next=head.next; head.next=cur;//头插法,将节点插在head后 cur=pos; } } } } public class Linked { public static void main(String[] args) { SingleLinekdListTakeHead<Integer> list=new SingleLinekdListTakeHead(); list.addHead(3); list.addHead(5); list.addHead(8); System.out.println(list.toString());//8 5 3 list.addTail(1); list.addTail(2); list.addTail(4); System.out.println(list.toString());//8 5 3 1 2 4 list.reverse(); System.out.println(list.toString()); // list.addPos(2, 100); //在2 号位置加入元素100 // System.out.println(list.toString()); // list.addPos(0, 1000); // System.out.println(list.toString()); // // list.remove(4); // System.out.println("删除值为4的元素:"+list.toString()); // // list.set(2,2);//true,把2号元素的值改为2 // System.out.println("把2号元素的值改为2:"+list.toString()); // System.out.println(list.get(3)); SingleLinekdListTakeHead list1=new SingleLinekdListTakeHead(); list1.addTail(2); list1.addTail(6); list1.addTail(7); SingleLinekdListTakeHead list2=new SingleLinekdListTakeHead(); list2.addTail(3); list2.addTail(4); list2.addTail(5); list2.addTail(8); list2.addTail(9); list2.addTail(10); list1.merge(list2); System.out.println(list1.toString()); } }

     练习5:链式队列

    package Exercise;
    
    public class LinkedQueue<T> {
        private Entry<T> front;
        private Entry<T> rear;
        private int count;
        public LinkedQueue(){
            this.front=this.rear=new Entry<>(null,null);
        }
        class Entry<T>{
            T data;
            Entry<T> next;
            public Entry(T data,Entry<T> next){
                this.data=data;
                this.next=next;
            }
        }
        public void offer(T data){
            Entry<T> node=new Entry<>(data,null);
            this.rear.next=node;
            this.rear=node;
            this.count++;
        }
    /**
    *出队列需要判断队列空的情况,头节点无效;如果队列为空,需要将front和rear都指向空
    */
    public void poll(){ if(this.front.next!=null){ this.front.next=this.front.next.next; if(this.front.next == null){ this.rear = this.front; } this.count--; } } public int size(){ return this.count; } public T peek(){ return this.front.next.data; } public void show(){ Entry<T> cur=this.front.next; while (cur!=null){ System.out.print(cur.data+" "); cur=cur.next; } System.out.println(); } public static void main(String[] args) { LinkedQueue l=new LinkedQueue(); for (int i = 0; i < 4; i++) { l.offer(i); } l.show(); System.out.println("队头元素为:"+l.peek()); System.out.println("队列长度为:"+l.size()); l.poll(); l.show(); } }

    难点:内部类和外部类的构造函数都需要对相应属性做初始化。

             对头和尾部节点的初始化:this.front=this.rear=new Entry<>(null,null);

              定义一个索引节点,指向头结点,不能用头结点自己去遍历: Entry<T>  cur=this.front.next;

              需要自定义打印函数show()以及show的实现

  • 相关阅读:
    ios swift 支持cocoaPods
    iOS 国际化
    ios storyboard全解析 (二)
    ios storyboard全解析 (一)
    UML类图的几个关系自我总结,(入门级)
    crypt 病毒
    js思维导向图
    关于索引的使用
    SQL Server 索引结构及其使用
    关于js数组的那些事
  • 原文地址:https://www.cnblogs.com/laurarararararara/p/11931685.html
Copyright © 2011-2022 走看看