234.回文链表
方法一:找中间结点,断开链表,后一段链表进行反转
思路:①找中间结点:使用快慢指针fast,slow,fast每次走两个,slow每次走一个;
如果链表的个数是奇数个,那么最后slow指向中间节点
如果链表的个数是偶数个,那么最后slow指向中间两个节点的后一个
②使用prev指针保存slow的前一个结点,然后prev.next = null 将链表分成前后两段
③将后一段链表进行反转,然后两段链表进行比对
/**
* @param {ListNode} head
* @return {boolean}
通过快慢指针找到中间节点,然后将链表分成前后两段 将后段链表进行反转 然后比较
奇数个结点,最后slow指向中间的结点;偶数个结点,最后slow指向中间两个结点的后一个
*/
var isPalindrome = function(head) {
if(head==null||head.next==null){
return true
}
let fast = head
let slow = head
let prev
while(fast&&fast.next){
prev = slow
slow = slow.next
fast = fast.next.next
}
//将链表断开
prev.next = null
//将后半段链表进行反转
let tempNode = null
while(slow){
let nextNode = slow.next
slow.next = tempNode
tempNode = slow
slow = nextNode
}
//进行比较 两个链表 如果原来的链表是奇数个 那么分段之后的链表 后一段会多一个结点
let head2 = tempNode
while(head&&head2){
if(head.val!==head2.val){
return false
}
head = head.next
head2 = head2.next
}
return true
};
方法二:遍历链表,将val保存到数组中,然后使用reverse()反转数组,然后进行比对
注意:reverse()会改变原来的数组,这里使用slice()创建了一个新的数组进行反转
/**
* @param {ListNode} head
* @return {boolean}
将链表的值全都放入数组中,然后进行数组的反转 reverse() 会改变原数组
*/
var isPalindrome = function(head) {
if(head==null||head.next==null){
return true
}
let cur = head
let res = []
while(cur!==null){
res.push(cur.val)
cur = cur.next
}
//reverse() 会改变原数组 于是想着使用formerArr来保存原来的数组 这样是不对的 res:数组和对象是复杂类型,变量中保存的是地址
// let formerArr = res 这样是不对的 当res变化的时候 formerArr也会变化
//使用slice()来创建一个新的数组 这样就不会改变原来的数组
let reverseArr = res.slice().reverse()
for(let i=0;i<res.length;i++){
if(res[i]!==reverseArr[i]){
return false
}
}
return true
};
方法三:同样是将val保存到数组中,这里不反转数组
思路:保存到数组之后,从数组首尾进行比对
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
将链表的值全都放入数组中,然后进行数组的反转 reverse() 会改变原数组
*/
var isPalindrome = function(head) {
if(head==null||head.next==null){
return true
}
let cur = head
let arr = []
while(cur!==null){
arr.push(cur.val)
cur = cur.next
}
let start = 0,end = arr.length - 1
while(end>=start){
if(arr[start]!==arr[end]){
return false
}
start++
end--
}
return true
};
9.回文数
var isPalindrome = function(x) {
//转换成字符串
let str = x.toString()
let i = 0
let tempStr = ''
//进行反转
while(i<str.length){
tempStr = str.charAt(i) + tempStr
i++
}
if(tempStr === str){
return true
}else{
return false
}
};
5.最长回文子串
方法一:中心元素拓展法
var longestPalindrome = function(s) {
//中心扩展法 直接拼接字符串
let maxStr = ''
//下标为i的 都可以作为一次中心元素 向两侧进行拓展
for(let i=0;i<s.length;i++){
let left = i-1
let str = s[i]
//如果遇到相同的元素,就像将其拼接上 res:相同的元素总是对称的 然后向两侧进行拓展
while(s[i+1]==s[i]){ //这里不用考虑越界的问题,如果i+1越界了,那么这里就不会相等
str = str + s[i+1] //如果遇到相邻的一连串相同的元素,都要将其加上,如果不加上的话,一定比全都有的情况要短
i++ //因为相同的连续元素都要加上,这里就直接i++了,不用重复进行遍历了
}
//中心元素右侧
let right = i+1
while(s[left]==s[right]&&s[left]!==undefined){//要防止left越界 这里right如果越界了,那么等号就不成立了
str = s[left] + str + s[right]
left--
right++
}
if(str.length>maxStr.length){
maxStr = str
}
}
return maxStr
};
中心元素拓展法优化
var longestPalindrome = function(s) {
//最大长度
let max = 1
//字符串开始的位置
let now = 0
//下标为i的 都可以作为一次中心元素 向两侧进行拓展
for(let i=0;i<s.length;i++){
let left = i-1
//当前长度
let curNum = 1
//如果遇到相同的元素,就像将其拼接上 res:相同的元素总是对称的 然后向两侧进行拓展
while(s[i+1]==s[i]){ //这里不用考虑越界的问题,如果i+1越界了,那么这里就不会相等
curNum++ //如果遇到相邻的一连串相同的元素,都要将其加上,如果不加上的话,一定比全都有的情况要短
i++ //因为相同的连续元素都要加上,这里就直接i++了,不用重复进行遍历了
}
//中心元素右侧
let right = i+1
while(s[left]==s[right]&&s[left]!==undefined){//要防止left越界 这里right如果越界了,那么等号就不成立了
curNum+=2
left--
right++
}
if(max<curNum){
max = curNum
//为啥要加1 因为在向两则进行拓展的时候,left先减一 然后再去判断
now = left + 1
}
}
return s.slice(now,now+max)
};
方法二:动态规划
var longestPalindrome = function(s) {
//动态规划 使用一个二维数组来保存 i到j之间的字符串是否是回文字符串
let len = s.length
//创建一个二维数组 如果arr[i][j] = 1 那么i到j之间是回文字符串
let arr = new Array(len).fill(0).map(item => item = new Array(len).fill(0))
//记录最大长度
let max = 0
//记录最大长度字符串的起始位置
let maxStart = 0,maxEnd = 0
let r = 0,l = 0
for( r = 1;r<len;r++){
for( l = 0;l<r;l++){
// r-l<=2 最多有三个元素
if(s[l]==s[r]&&(r-l<=2 || arr[l+1][r-1]==1)){
arr[l][r] = 1
if(max<r-l+1){
max = r-l+1
maxStart = l
maxEnd = r
}
}
}
}
return s.slice(maxStart,maxEnd+1)
};