判断单链表保存的字符串是否是回文字符串

问题

如果字符串存储在单链表中,怎么快速判断这个字符串是否是回文字符串?

知识点

快慢指针、链表反转

实现思路

一般方法 复杂度为O(n²)

  1. 如果字符串存储在数组中,那么只需要判断第一个和最后一个这么一一对应下去,判断字符是否相同,还是比较简单的。
  2. 如果单链表中存储也参照这种方法,那么可以定义两个指针,一个初始指向头部,一个初始指向尾部,后续依次一个向后指一个依次向前指。这样也能判断是否是回文字符串。但是对于单链表,查找前继节点,需要遍历链表,所以复杂度是O(n²)

复杂度为O(n)的方法

  1. 如果使用单链表存储,那么第一步需要找到链表的中间节点,这一步需要用到快慢指针
  2. 第二步需要将单链表的后半部分进行反转,单链表的反转

代码

定义链表结构
/**
 * 单链表的节点类
 *
 * @author StoneYu
 * @date 2022/09/25
 */
@Data
@AllArgsConstructor
public class SinglyLinedNode<T> {
    T data;
    SinglyLinedNode<T> next;

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public SinglyLinedNode<T> getNext() {
        return next;
    }

    public void setNext(SinglyLinedNode<T> next) {
        this.next = next;
    }
}

@Data
public class SinglyLinkedList<T> {

    /**
     * 单链表的头指针
     */
    SinglyLinedNode<T> head;

    /**
     * 从尾部添加一个节点
     *
     * @param t t
     */
    public void add(SinglyLinedNode<T> t) {
        if (t == null) {
            return;
        }
        if (head == null) {
            head = t;
            return;
        }

        SinglyLinedNode<T> couser = head;
        while (couser.next != null) {
            couser = couser.next;
        }
        couser.next = t;
    }

    /**
     * 打印链表内容
     *
     * @return {@link String}
     */
    public String getString() {
        if (head == null) {
            return null;
        }
        StringBuilder stringBuilder = new StringBuilder();

        SinglyLinedNode<T> couser = head;
        stringBuilder.append("链表:" + (couser.getData()==null?"null":couser.getData()));
        while (couser.next != null) {
            couser = couser.next;
            stringBuilder.append("->" + (couser.getData()==null?"null":couser.getData()));
        }
        return stringBuilder.toString();
    }

}
从指定的位置反转单链表
/**
* 从第 index+1 的元素开始反转,0反转整个链表
*
* @param singlyLinkedList 单链表
* @param index            下标 >0
*/
static SinglyLinkedList reverseALinkedList(SinglyLinkedList singlyLinkedList, int index) {
    //空、或者只有1个元素
    if (singlyLinkedList.head == null || singlyLinkedList.head.next == null) {
        return singlyLinkedList;
    }
    if (index <= 0) {
        index = 0;
    }


    //反转尾部的链表进行比较
    SinglyLinedNode cursor = new SinglyLinedNode(null, singlyLinkedList.head);
    int temp = 0;
    //begin作为一个伪头结点
    SinglyLinedNode begin = new SinglyLinedNode(null, singlyLinkedList.head);
    if (index == 0) {
        singlyLinkedList.head = begin;
    }
    //mid作为截取队列的的首个节点
    SinglyLinedNode mid = null;
    //end是需要进行头插的节点
    SinglyLinedNode end = null;

    while (cursor.next != null) {
        //遍历链表
        cursor = cursor.next;
        temp++;

        if (temp == index) {
            begin = cursor;
        }
        if (temp - 1 == index) {
            mid = cursor;
            end = cursor.next;
        }

        //反转半个链表,就地逆置法
        if (temp > index && end != null) {
            //将需要逆序的元素,直接作为头结点的后继节点
            mid.next = end.next;
            end.next = begin.next;
            begin.next = end;
            //保持遍历的顺序,将游标往后移动
            cursor = mid;
            //如果还有需要进行头插的元素,则end指针需要往后移动一下
            if (mid.next != null) {
                end = mid.next;
            }
        }
    }
    if (index == 0) {
        singlyLinkedList.head = begin.next;
    }

    SinglyLinkedList<Object> objectSinglyLinkedList = new SinglyLinkedList<>();
    objectSinglyLinkedList.setHead(begin.next);

    return objectSinglyLinkedList;
}
最终方法
/**
 * 单链表结构判断字符串是否是回文字符串
 * 一般方法 复杂度为O(n²)
 * 1. 如果字符串存储在数组中,那么只需要判断第一个和最后一个这么一一对应下去,判断字符是否相同,还是比较简单的。
 * 2. 如果单链表中存储也参照这种方法,那么可以定义两个指针,一个初始指向头部,一个初始指向尾部,后续依次一个向后指一个依次向前指。这样也能判断是否是回文字符串。但是对于单链表,查找前继节点,需要遍历链表,所以复杂度是O(n²)
 * 复杂度为O(n)的方法
 * 1. 如果使用单链表存储,那么第一步需要找到链表的中间节点,这一步需要用到快慢指针
 * 2. 第二步需要将单链表的后半部分进行反转,单链表的反转
 *
 * @author StoneYu
 * @date 2022/09/25
 */
public class TestHuiWenZiFuChuan {

    public static void main(String[] args) {
        String testStr = "1";
        SinglyLinkedList<Character> singlyLinkedList = transStringToLinkedList(testStr);
        System.out.println(singlyLinkedList.getString());

        //查找链表的中间节点的下标 快慢指针
        SinglyLinedNode slow = singlyLinkedList.head;
        SinglyLinedNode quick = singlyLinkedList.head;
        int index = 1;
        while (quick.next != null) {
            slow = slow.next;
            quick = quick.next;
            if (quick.next == null) {
                break;
            }
            quick = quick.next;
            index++;
        }
        SinglyLinkedList singlyLinkedList1 = reverseALinkedList(singlyLinkedList, index);
        System.out.println("中间位置为:"+index);
        System.out.println("从中间反转后原链表:" + singlyLinkedList.getString());
        System.out.println("反转后截取的链表:" + singlyLinkedList1.getString());
        //定义两个指针挨个比较
        System.out.println("需要比较的节点数量中间数为"+(index%2==0?"偶数则-1":"奇数则不变")+":"+(index%2==0?index-1:index));
        int needConpare=index%2==0?index-1:index;

        int hasCompare=0;
        SinglyLinedNode<Character> cursor1=new SinglyLinedNode(null,singlyLinkedList.head);
        SinglyLinedNode<Character> cursor2=new SinglyLinedNode(null,singlyLinkedList1.head);
        for (int i = 0; i <needConpare; i++) {
            Character data1 = cursor1.next.data;
            Character data = cursor2.next.data;
            if (!data1.equals(data)){
                System.out.println("\n\n判断结果:"+testStr+"不是回文字符串");
                return;
            }

        }
        System.out.println("\n\n判断结果:"+testStr+"是回文字符串");


    }


    


    /**
     * 将字符串转换为单链表
     *
     * @param targetStr 目标str
     * @return {@link SinglyLinkedList}<{@link Character}>
     */
    static SinglyLinkedList<Character> transStringToLinkedList(String targetStr) {
        if (targetStr == null || targetStr.trim().equals("")) {
            return null;
        }
        char[] chars = targetStr.toCharArray();
        SinglyLinkedList<Character> characterSinglyLinkedList = new SinglyLinkedList<>();
        for (Character aChar : chars) {
            characterSinglyLinkedList.add(new SinglyLinedNode<>(aChar, null));
        }
        return characterSinglyLinkedList;

    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值