数据结构与算法-回文(动态规划、快慢指针)

回文

本文会先从LeetCode的第五题开始讲起,使用动态规划去解决这道题,然后进一步扩展到 “ 如果回文串是单链表数据结构,那么如果判断是否为回文串? ”。
也就是说,对于LeetCode第五题,我会用动态规划的方法去解决;
对于单链表回文题,我不仅会用动态规划的方法去解决,还会使用快慢指针法去解决。

一、动态规划解决LeetCode第五题

数据结构与算法-回文(动态规划、快慢指针)_第1张图片
动态规划类题型都有着非常固定的解题步骤,如下图所示:
数据结构与算法-回文(动态规划、快慢指针)_第2张图片
所以我们对应着进行三步的考虑:

第一步是去明确dp[i][j]的含义,这里如果字符串i~j是回文串,则dp[i][j] = True,否则为False。

第二步是找到二维dp数组之间的关系式,回文的特点即是首尾元素必定相等。所以我们可以进一步考虑:
如果dp[i+1][j-1]为True, 此时如果si = sj,则dp[i][j] = dp[i+1][j-1]。
注意这里的 i+1 和 j-1
dp[i][j] 若指代的字符串为 “abc”,则 dp[i+1][j-1] 指代的字符串为 “b”。

第三步是找到初始条件,先不考虑边界条件,字符串个数为1时,我们设其为True, 即 dp[i][i] = True。
那么 i+1 和 j-1 最终会逼近成为单个字符串,也就是说当 dp[i+1][j-1] 此时指代的字符串个数如果为1,我们设其为True。
dp[i+1][j-1]指代的字符串长度为(j-1)-(i+1)+1,其值需要小于2,所以可得j-i<3时,为单个字符串,此时的dp元素为True

接下来上动态规划核心判断回文的代码:

size = len(string)
# dp元素只是判断字符串是否为回文,
# 所以我们依旧要罗列所有的i、j可能性
for j in range(1, size):
	for i in range(0, j):
		if s[i] == s[j]:  # 判断首尾
			if j-i < 3:  # 判断是否是单个字符串
				dp[i][j] = True
			else:  # 如果不是单个字符串,则可继续逼近
				dp[i][j] = dp[i+1][j-1]
				# j之前的所有元素均已经存储在dp中了
		else:
			dp[i][j] = False

再上完整的动态规划解决最长回文子串问题的代码:

class Solution(object):
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        size = len(s)
        if size < 2:
            return s
        
        dp = [[False for _ in range(size)] for _ in range(size)]  # 二维dp数组

        for i in range(size):
            dp[i][i] = True

        start = 0
        max_len = 1  # 考虑输入"ac",返回"a"的情况,所以为1

        for j in range(1, size):
            for i in range(0, j):
                if s[i] == s[j]:
                    if j-i<3:
                        dp[i][j] = True
                    else:
                        dp[i][j] = dp[i+1][j-1]
                else:
                    dp[i][j] = False
                
                if dp[i][j]:
                    if j-i+1 > max_len:
                        max_len = j-i+1
                        start = i
        return s[start:start+max_len]

二、若是单链表形式的回文,那也仅仅只是存储数据方式的不同而已。如下图

三、使用快慢指针法判断单链表存储的字符串是否为“回文串”

刚刚都是使用动态规划来对字符回文串以及单链表回文去进行解决,这里使用快慢指针法对单链表回文进行解决。

设三个指针和一个隐藏指针,

prev = None
slow = head  # 慢指针每次走一步,同时将逆序传入prev
fast = head  # 快指针每次走两步,fast = fast.next.next
# 还有一个隐藏指针next,用于处理prev和slow的交互

需要分奇数和偶数来进行分别讨论,

若为奇数,则
第一次循环图解如下:

再来第二次循环,刚好可以仔细考虑下边界情况:

若考虑偶数的情况:

最后上完整的 快慢指针法判断单链表是否是回文 的核心代码:

if (head == None || head.next == None):
	return True
	
prev = None
slow = head
fast = head

while (fast != None && fast.next != None):  # 奇偶都考虑
	fast = fast.next.next
	ListNode next = slow.next
	slow.next = prev
	prev = slow
	slow = next
# 跳出循环进入边界

if (fast != None):  # 针对奇数情况的微调
	slow = slow.next

while (slow != None):  # 判断时奇偶都一样,slow指向Null时跳出循环
	if (slow.val != prev.val):
		return False  
	slow = slow.next
	prev = prev.next
	
return True

你可能感兴趣的:(数据结构与算法)