一、移除鏈表中指定的節點
題目:
/** * 刪除鏈表中等於給定值 val 的所有節點。 * * 示例: * 輸入: 1->2->6->3->4->5->6, val = 6 * 輸出: 1->2->3->4->5 */
題的描述很簡單,做起來並不容易,有一些小坑,要注意的就是頭尾的節點的處理和連續相等的情況處理,其它的就非常簡單了,遍歷鏈表,將與val相等的前一個節點和下一節點相連。
處理這樣的問題肯定會用到while循環,這樣更方便也更不容易出錯。
因爲要處理頭結點就是目標節點的情況,所以你可以在開頭就加上一個while循環來過濾
在循環中你可以檢測下一個節點的val是否等於目標值,如果相等就while判斷下一個節點的val是不是目標,直到找到不是的那一個,連接之前的節點,爲此你要定義一個局部變量方便操作
在其中你還要注意加上判斷以防止尾節點的問題
public static ListNode method1(ListNode head, int val) {
while (head!=null&&head.val==val){
head=head.next;
}
if (head==null){
return null;
}
ListNode operation=head;
while (operation.next!=null){
if (operation.next.val==val){
if (operation.next.next==null){
operation.next=null;
}else {
ListNode transientNode=operation.next;
while (transientNode!=null&&transientNode.val==val){
transientNode=transientNode.next;
}
if (transientNode==null){
operation.next=null;
}else {
operation.next=transientNode;
operation=transientNode;
}
}
}else {
operation=operation.next;
}
}
return head;
}
這樣的代碼很長很繁瑣,還有一堆判斷,我們是否可以有一個更好的辦法來優化算法
你要考慮頭結點的問題,這個問題你可以自己給它加上一個頭結點,這樣就不用再爲頭結點的處理加一個循環了
你要考慮連續結點值都是目標值的問題,你可以採用這樣的策略,如果下一個節點是目標值,那麼就連接next.next,否則向前移動。這樣的話即使是連續目標值也能得到判斷
public static ListNode method2(ListNode head,int val){
ListNode header = new ListNode(-1);
header.next = head;
ListNode cur = header;
while(cur.next != null){
if(cur.next.val == val ){
cur.next = cur.next.next;
}else{
cur = cur.next;
}
}
return header.next;
}
這樣一看代碼是不是就減少了很多了,思路也很清晰,幾乎所有的while循環的問題我們都可以用遞歸解決
我們要關注的問題就是當前節點的val值是不是目標值,如果是就返回next,不是就返回本身
public static ListNode method3(ListNode head,int val){
if(head == null) return null;
head.next = method3(head.next, val);
return head.val == val ? head.next : head;
}
public static class ListNode {
int val;
ListNode next;
ListNode(int x) { val = x; }
}
是不是思路更爲清晰了呢?
二、質數的數量
題目:
/** * 統計所有小於非負整數 n 的質數的數量。 * * 示例: * 輸入: 10 * 輸出: 4 * 解釋: 小於 10 的質數一共有 4 個, 它們是 2, 3, 5, 7 。 */
入門的時候肯定學過如何判斷一個數是不是質數,也學過一些優化,那就一個一個判斷嘛,所以就寫下了這段代碼
public static int method1(int n) {
int count=0,j=0,k=0;
for (int i=2;i<=n;i++){
k=(int)sqrt(i+1);
for (j=2;j<=k;j++){
if (i%j==0){
break;
}
}
if (j==k+1){
count++;
}
}
return count;
}
然而事情並沒有那麼簡單,它是有時間限制的,給出了一個5000000的大數,我就懵了,仔細研究,一定有什麼訣竅
寫下20個數,仔細觀察,你會發現質數的倍數都不是質數(很傻缺的結論),但是你就可以用這個結論來屆這道題,如果你檢測到3是一個質數,那麼之後的6,9,12,15,18.。。。。就都不是質數了,都不用判斷了,這就節省了非常多的判斷。
public static int method2(int n){
boolean[] isPrime = new boolean[n];
for (int i = 2; i < n; i++) {
isPrime[i] = true;
}
for (int i = 2; i * i < n; i++) {
if (!isPrime[i]) continue;
for (int j = i * i; j < n; j += i) {
isPrime[j] = false;
}
}
int count = 0;
for (int i = 2; i < n; i++) {
if (isPrime[i]) count++;
}
return count;
}
三、同構字符串
題目:
/** * 給定兩個字符串 s 和 t,判斷它們是否是同構的。 * 如果 s 中的字符可以被替換得到 t ,那麼這兩個字符串是同構的。 * 所有出現的字符都必須用另一個字符替換,同時保留字符的順序。兩個字符不能映射到同一個字符上,但字符可以映射自己本身。 * * 示例 1: * 輸入: s = "egg", t = "add" * 輸出: true * * 示例 2: * 輸入: s = "foo", t = "bar" * 輸出: false * * 示例 3: * 輸入: s = "paper", t = "title" * 輸出: true */
滿足同構字符串有一個關鍵的條件,那就是字符與字符之間必須是一對一的關係,不能一對多也不能多對一。那我們就使用HashMap建立這樣的一對一的映射關係,最後遍歷字符串看看是否對應正確。
public static boolean method1(String s, String t) {
if (s.length()!=t.length())return false;
HashMap<Character,Character> hashMap=new HashMap<>();
char[] chars=s.toCharArray();
char[] chart=t.toCharArray();
for (int i=0;i<chars.length;i++){
if (!hashMap.keySet().contains(chars[i])&&hashMap.values().contains(chart[i])){
return false;
}
hashMap.put(chars[i],chart[i]);
}
for (int i=0;i<chars.length;i++){
if (!hashMap.get(chars[i]).equals(chart[i])){
return false;
}
}
return true;
}
這樣要遍歷字符串兩遍,可以優化一下,使它遍歷一遍。要完成的任務爲一對一的字符映射,藉助HashMap,可以先檢測鍵中是否含有該字符,含有的話取出來看看映射的是否正確,沒有的話看看值中是否含有對應字符,含有就說明對應關係錯誤。這樣一遍遍歷就搞定了。
public static boolean method2(String s, String t) {
if (s.length()!=t.length())return false;
HashMap<Character,Character> hashMap=new HashMap<>();
char[] chars=s.toCharArray();
char[] chart=t.toCharArray();
for (int i=0;i<chars.length;i++){
if (hashMap.keySet().contains(chars[i])){
if (!hashMap.get(chars[i]).equals(chart[i])){
return false;
}
}else {
if (hashMap.values().contains(chart[i])){
return false;
}else {
hashMap.put(chars[i],chart[i]);
}
}
}
return true;
}
還能更爲優化一下,我們是藉助HashMap來完成字符的一對一映射的,HashMap的底層是使用數組實現的,而字符剛好可以轉換爲Ascll碼當做數組下標來使用,核心思想還是一樣,但這樣性能上就更爲提升了一截。
public static boolean method4(String s,String t){
char[] sc = s.toCharArray();
char[] tc = t.toCharArray();
char[] map = new char[256];
for (int i = sc.length-1;i >= 0;i--) {
if (map[sc[i]] != map[tc[i]+128]) {
return false;
}
map[sc[i]] = map[tc[i] + 128] = sc[i];
}
return true;
}
更換一種思路,兩個字符串相互判斷,對兩個結果使用與連接。不在內部處理一對一,而是簡單的檢查多對一,相互判斷之後的結果也是一對一。
public static boolean method3(String s,String t){
return isIsomorphic(s,t) && isIsomorphic(t,s);
}
private static boolean isIsomorphic(String t, String s) {
char[] dict = new char[256];
Arrays.fill(dict, (char) 0);
for (int i = 0; i < s.length(); i++) {
if (dict[s.charAt(i)] == (char) 0) {
dict[s.charAt(i)] = t.charAt(i);
} else if (dict[s.charAt(i)] != t.charAt(i)) {
return false;
}
}
return true;
}
四、重複元素
題目:
/** * 給定一個整數數組,判斷是否存在重複元素。 * 如果任何值在數組中出現至少兩次,函數返回 true。如果數組中每個元素都不相同,則返回 false。 * * 示例 1: * 輸入: [1,2,3,1] * 輸出: true * * 示例 2: * 輸入: [1,2,3,4] * 輸出: false * * 示例 3: * 輸入: [1,1,1,3,3,4,3,2,4,2] * 輸出: true */
這道題可以說是相當簡單了,可以直接使用我們熟知的數據結構Set來進行結題
public static boolean method1(int[] nums) {
HashSet<Integer> integerHashSet=new HashSet<>();
for (int a:nums){
integerHashSet.add(a);
}
if (integerHashSet.size()!=nums.length){
return true;
}else {
return false;
}
}
但我們還是要用一些花樣來解這道題,學習嘛,使用Arrays的排序方法對數組排序,這樣相同的元素就被放到一起了,然後for循環~
public static boolean method2(int[] nums){
Arrays.sort(nums);
for (int i=0;i<nums.length-1;i++){
if (nums[i]==nums[i+1]){
return true;
}
}
return false;
}
學過java8的同學可能有一些新思路,使用流處理,分組,計數,看看是不是和數組長度相等。使用流不關注實現只關注結果,這樣一行代碼就搞定了,但實際上他的效率並不高。
public static boolean method3(int[] nums){
return Arrays.stream(nums).distinct().count() != nums.length;
}