第一題 輸出鏈表中的倒數第n個結點
給出的結構體為:
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
我覺得首先應該要把整個鏈表的長度先求出來,然后在不判斷不遍歷整個鏈表的情況下只比對數字。下面是示例代碼:
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
ListNode* temp = pListHead;
int count=0;
while(temp){
count++;
temp=temp->next;
}
for(int i=0;i<count;){
if(i == count-k){
return pListHead;
break;
}
pListHead=pListHead->next;
i++;
}
return NULL;
}
};
第二題 刪除單向鏈表中的一個節點
我覺得刪除節點分兩步,一個是刪除單向鏈表中上一個只想該節點的鏈接和該節點指向下一個節點的鏈接,而變成第n-1個節點->next指向原先的n+1個節點。第二步就是當刪除了鏈接并不代表當前刪除的節點不占用內存空間,所以就要free這個空間。下面是通過的代碼示例:
class Remove {
public:
bool removeNode(ListNode* pNode) {
// write code here
if(pNode->next == NULL)
return false;
ListNode* temp=pNode->next;
pNode->next=temp->next;
free(temp);
return true;
}
};
第三題 分割鏈表
題目描述的是要將小于給定x值的節點與大于x值的節點分割成兩部分,而本身鏈表的順序不能夠改變。最后需要返回小于x值的鏈表+大于x值的鏈表的頭節點。
想法很單純,既然要分割鏈表就自然需要定義兩個新的鏈表,第一步遍歷整個給定的鏈表將每個節點的值與給定的x值作對比,當小于x的時候就把新定義的p1鏈表的下一個節點指向此時的temp節點,而當大于x值得時候自然就將此時的temp節點賦給p2的下一個節點。
在過程中需要保存兩個新定義鏈表的表頭,也就是要判斷一下p1和p2的頭結點以便最后返回頭節點。下面為最終的代碼示例:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition {
public:
ListNode* partition(ListNode* pHead, int x) {
// write code here
ListNode* p1 = NULL;
ListNode* p2 = NULL;
ListNode* temp = NULL;
ListNode* p1Head = NULL;
ListNode* p2Head = NULL;
while(pHead){
temp = pHead->next;
if(pHead->val<x){
if(p1 == NULL){
p1=pHead;
p1Head=pHead;
}
else{
p1->next=pHead;
p1=p1->next;
}
}
else{
if(p2 == NULL){
p2=pHead;
p2Head=pHead;
}
else{
p2->next=pHead;
p2=p2->next;
}
}
pHead=temp;
}
if(p2)
p2->next=NULL;
if(p1 == NULL)
p1Head=p2Head;
else
p1->next=p2Head;
return p1Head;
}
};
第四題 鏈表A+B
題目描述為相加兩個給出的鏈表A與B,比如說:{1,2,3} + {3,2,1}需要返回一個結果鏈表為{4,4,4}。
在中間的加法運算中主要分為兩步,第一步計算每每兩個數字相加的結果,第二部提取出相應結果的進位和需要保留的個位數結果。下面為這一部分實現的代碼示例:
val1 = a?a->val:0;
val2 = b?b->val:0;
int val = val1 + val2+carry;
carry = val/10;
定義兩個新的ListNode結構的新鏈表,一個用來做最后返回的鏈表頭,一個作為鏈表的存儲。因為不知道兩個給定的鏈表長度,所以判斷的時候兩個鏈表都判斷是否為NULL再進入循環來遍歷兩個鏈表。當遍歷時判斷一下最終返回鏈表的頭節點,下面為完整的代碼示例:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class Plus {
public:
ListNode* plusAB(ListNode* a, ListNode* b) {
// write code here
ListNode* pNode = NULL;
ListNode* pHead = NULL;
int carry=0;
if(a == NULL && b == NULL)
return NULL;
while(a || b || carry>0){
int val1,val2;
val1 = a?a->val:0;
val2 = b?b->val:0;
int val = val1 + val2+carry;
carry = val/10;
ListNode* temp = new ListNode(val%10);
if(pNode == NULL){
pNode = temp;
pHead = pNode;
}
else{
pNode->next = temp;
pNode=pNode->next;
}
a=a?a->next:NULL;
b=b?b->next:NULL;
}
pNode->next = NULL;
return pHead;
}
};
第五題 回文鏈表
題目描述是這樣的,需要判斷一個給定的鏈表是否為回文鏈表,若給定鏈表如{1,2,3,2,1}則返回true,反之返回false。想法應該說并不難,但是我在鏈表的地址和賦值之間來回徘徊了很久才大概明白代碼測試一直不能通過是因為什么,下面先來看這個代碼
class Palindrome {
public:
bool isPalindrome(ListNode* pHead) {
// write code here·
ListNode* reverseNode = NULL;
ListNode* originalNode = pHead;
ListNode* temp = NULL;
ListNode* next = NULL;
if(pHead == NULL)
return false;
while(pHead){
next = pHead->next;
temp = reverseNode;//temp=current
reverseNode = pHead;//prev=current
reverseNode->next = temp;//next=current
pHead = next;
}
while(originalNode && reverseNode){
if(originalNode->val != reverseNode->val){
return false;
break;
}
originalNode = originalNode->next;
reverseNode = reverseNode->next;
}
return true;
}
};
首先定義了兩個鏈表節點分別指向第一個給定節點和NULL,然后遍歷給定的鏈表將鏈表反轉。在遍歷的過程中,將reverse這個想象中是反轉后的鏈表先傳給一個temp以做保留,將reverse指向遍歷當前的節點,而reverse->next指向先前的地址也就是temp,然而此時我想也就破壞了給定鏈表指向next的這個節點鏈接了。
先貼一個正常反轉鏈表并返回反轉之后鏈表表頭的leetcode上通過的代碼:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* temp,*current,*next;
ListNode* reverse = NULL;
current = head;
//prev = NULL;
while(current){
next = current->next;
temp = reverse;
reverse = current;
reverse->next = temp;
current = next;
}
return reverse;
}
};
同樣將這段代碼整齊的貼進這道題再去遍歷做比較,依然會不能通過測試,始終返回true,以我的專業出身和智商還沒有真正深究出原因,不過我猜想是因為永遠指向同一個地址那么比較的時候肯定會不如人意,當在反轉時有必要將反轉之后的鏈表reverse和在遍歷當前給定鏈表的時候new一個新的ListNode而不是在同一條地址鏈上進行多次的重復操作以至于最后比對的original鏈表丟失。這也是為什么當只是單純做反轉鏈表的時候遍歷第一步就要保存好當前節點next指向的地址。
所以重新定義并new一個鏈表reverse為NULL,然后遍歷整個給定的鏈表,將當前節點值給該次循環中的reverse節點,將該被改動的reverse節點的next指向上一循環中的reverse節點,以此類推,直到把NULL推到最后一個位置。這個方法我很熟悉,跟做硬件編譯時傳輸10101010....類的數據相差不大,都是按照一定的順序你推我我推你到窮盡。
這里很想插一句
一直以來人們給所謂程序員配的圖滿是10101010.....,看黑客帝國就知道了,我想說沒錯,計算機是接受二進制,可是在我自學Nodejs開發和這些滿滿的上層軟件面向對象碰到過10101010現象的機會遠遠不如我作為公司的一名新晉嵌入式開發來的平常。
程序員分很多種,我希望我哪種都不是,我不是一名程序員或者軟件、硬件工程師,我只是一名普通人,懂得少學得慢。
好吧,插了一句嘴,下面來貼上糾結了我一下午的最終通過代碼示例:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class Palindrome {
public:
bool isPalindrome(ListNode* pHead) {
// write code here
ListNode* temp = NULL;
ListNode* current = pHead;
ListNode* reverse = new ListNode(NULL);
if(pHead == NULL)
return false;
while(current){
//temp = reverse;
//reverse->next = temp;
//reverse = current->next;
temp=reverse;
ListNode* nextnode = new ListNode(current->val);//reverse = current->next;
reverse = nextnode;
reverse->next = temp;
current = current->next;
}
while(pHead){
if(pHead->val!=reverse->val)
{
return false;
break;
}
pHead=pHead->next;
reverse=reverse->next;
}
return true;
}
};
原文地址、戳我