[leetcode]🎉20200202🎉在回文日刷回文题

366 阅读4分钟

palindrome, 回文, 正反读都一样的词语

今天是2020年02月02日, 稀有的回文日, 回文题目走你

9. 回文数

思路与代码实现

解法一: 翻转数字

/**
 * @param {number} x
 * @return {boolean}
 */
 var isPalindrome = function(x) {
  if (x < 0) {
    return false
  } else if (x >= 0 && x < 10) {
      return true
  }  else {
      let reverse = 0
      let cur = x
      while (cur / 10 > 0) {
          reverse = reverse * 10 + cur % 10
          cur = Math.floor(cur / 10)
      }
      return reverse === x
  }
};

优化, 解法二: 翻转半截数字

/**
 * @param {number} x
 * @return {boolean}
 */
var isPalindrome = function(x) {
  // 负数
  if (x < 0) {
    return false
  // 0至9
  } else if (x >= 0 && x < 10) {
      return true
  // 末尾数为0的非零数字
  } else if (x !== 0 && x % 10 === 0) {
    return false
  } else {
      let reverse = 0
      // 如果翻转数字大于原数字, 则翻转到了中间数位
      while (x > reverse) {
          reverse = reverse * 10 + x % 10
          x = Math.floor(x / 10)
      }
      // 考虑奇数位
      return (reverse === x) || (Math.floor(reverse / 10) === x)
  }
};

866. 回文素数

思路与代码实现

验证回文数(参考前面的9. 回文数)和验证素数, 注意不存在八位的素数

/**
 * @param {number} N
 * @return {number}
 */
var primePalindrome = function(N) {
  if (N <= 2) {
    return 2
  } else {
    function isPrime (n) {
      let i = 2
      while (i <= Math.sqrt(n)) {
        if (n % i === 0) {
          return false
        }
        i++
      }
      return true
    }
    function isPalindrome (n) {
      if (n < 10) {
        return true
      } else if (n % 10 === 0) {
        return false
      } else {
        let reverse = 0
        while (n > reverse) {
          reverse = reverse * 10 + n % 10
          n = Math.floor(n / 10)
        }
        return (n === reverse) || (n === Math.floor(reverse / 10))
      }
    }
    while (!(isPalindrome(N) && isPrime(N))) {
      N++
      if (N > 10000000 && N < 100000000) {
        N = 100000000
      }
    }
    return N
  }
};

234. 回文链表

思路与代码实现

解法一: 双向链表

把链表变成双向链表, 并从两端向中间比较

复杂度分析

时间复杂度为O(n), 因为要存储prev指针, 所以空间复杂度为O(n)

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {boolean}
 */
var isPalindrome = function(head) {
  // 考虑空链表
  if (head === null) {
    return true
  } else {
    // 头指针
    let headPointer = head
    // 尾指针
    let tailPointer = head
    // 把链表变成双向链表
    while (tailPointer.next) {
      tailPointer.next.prev = tailPointer
      tailPointer = tailPointer.next
    }
    // 分别从头尾向中间依次比较
    while(headPointer !== tailPointer) {
      if (headPointer.next !== tailPointer) {
        if (headPointer.val === tailPointer.val) {
          headPointer = headPointer.next
          tailPointer = tailPointer.prev
        } else {
          return false
        }
      // 考虑偶数链表
      } else {
        return headPointer.val === tailPointer.val
      }
    }
    return true
  }
};

优化, 解法二: 快慢指针

  • 慢指针依次访问下一个节点, 并翻转链表
  • 快指针依次访问下下一个节点, 直到快指针没有下一个节点(奇数链表)或者下下一个节点(偶数链表), 此时已完成了前半截链表的翻转
  • 依次比较前半截链表和后半截链表节点的值
复杂度分析

时间复杂度O(n), 空间复杂度O(1)

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {boolean}
 */
var isPalindrome = function(head) {
  if (head === null) {
    return true
  } else if (head.next === null) {
    return true
  } else {
    let slowPointer = head
    let fastPointer = head
    let _head = null
    let temp = null
    // 找到中间节点, 并翻转前半截链表,
    // 让_head指向翻转后的前半截链表的头部,
    // 让slow指向后半截链表的头节点
    while (fastPointer && fastPointer.next) {
      _head = slowPointer
      slowPointer = slowPointer.next
      fastPointer = fastPointer.next.next
      _head.next = temp
      temp = _head
    }
    // 奇数链表跳过最中间的节点
    if (fastPointer) {
      slowPointer = slowPointer.next
    }
    while (_head) {
      if (_head.val !== slowPointer.val) {
        return false
      }
      _head = _head.next
      slowPointer = slowPointer.next
    }
    return true
  }
};

680. 验证回文字符串 Ⅱ

思路与代码实现

解法一: 双指针

从头尾比较, 若两字符不一相等, 则判断删除某一字符后的子串是否为回文

/**
 * @param {string} s
 * @return {boolean}
 */
var validPalindrome = function(s) {
  let headPointer = 0
  let tailPointer = s.length - 1
  function isPalindrome (s) {
    let headPointer = 0
    let tailPointer = s.length - 1
    while (headPointer < tailPointer) {
      if (s[headPointer] !== s[tailPointer]) {
        return false
      } else {
        headPointer++
        tailPointer--
      }
    }
    return true
  }
  while (headPointer < tailPointer) {
    if (s[headPointer] !== s[tailPointer]) {
      if (s[headPointer + 1] === s[tailPointer] || s[headPointer] === s[tailPointer - 1]) {
        return isPalindrome(s.substring(headPointer + 1, tailPointer + 1)) || isPalindrome(s.substring(headPointer, tailPointer))
      } else {
        return false
      }
    } else {
      headPointer++
      tailPointer--
    }
  }
  return true
};

优化, 解法二: 复用双指针

在解法一的基础上, 不截取子串, 继续利用双指针判断子串是否为回文

/**
 * @param {string} s
 * @return {boolean}
 */
var validPalindrome = function(s) {
  let headPointer = 0
  let tailPointer = s.length - 1
  function isPalindrome (s, headPointer, tailPointer) {
    while (headPointer < tailPointer) {
      if (s[headPointer] !== s[tailPointer]) {
        return false
      } else {
        headPointer++
        tailPointer--
      }
    }
    return true
  }
  while (headPointer < tailPointer) {
    if (s[headPointer] !== s[tailPointer]) {
      if (s[headPointer + 1] === s[tailPointer] || s[headPointer] === s[tailPointer - 1]) {
        return isPalindrome(s, headPointer + 1, tailPointer) || isPalindrome(s, headPointer, tailPointer - 1)
      } else {
        return false
      }
    } else {
      headPointer++
      tailPointer--
    }
  }
  return true
};

409. 最长回文串

思路与代码实现

解法一: 哈希表, 判断字符出现的奇偶

/**
 * @param {string} s
 * @return {number}
 */
var longestPalindrome = function(s) {
  const hashTable = {}
  let result = 0
  for(let i = s.length - 1; i >= 0; --i) {
    if (hashTable[s[i]] === undefined) {
      hashTable[s[i]] = 1
    } else {
      ++hashTable[s[i]]
    }
  }
  let flag = true
  for (let key in hashTable) {
    j = hashTable[key]
    // 偶数
    if ((j & 1) === 0) {
      result += j
    } else {
      // 奇数
      result += (j - 1)
      if (flag) {
        result++
        flag = false
      }
    }
  }
  return result
};

优化, 解法二: 哈希表, 记录成对的字符

/**
 * @param {string} s
 * @return {number}
 */
var longestPalindrome = function(s) {
  const len = s.length
  const hashTable = {}
  let result = 0
  for(let i = len - 1; i >= 0; --i) {
    if (hashTable[s[i]] === undefined || hashTable[s[i]] === 0) {
      hashTable[s[i]] = 1
    } else {
      result += 2
      hashTable[s[i]] = 0
    }
  }
  return result < len ? result + 1 : result
};