链表的经典习题

练习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的实现

posted @ 2019-11-25 23:10  acehm  阅读(597)  评论(0)    收藏  举报