diff --git a/C++PrimerTest/C++PrimerTest.cpp b/C++PrimerTest/C++PrimerTest.cpp new file mode 100644 index 00000000..4b28d052 --- /dev/null +++ b/C++PrimerTest/C++PrimerTest.cpp @@ -0,0 +1,70 @@ +#include +#include +#include +#include +using namespace std; + +struct CModel +{ +private: + int a; + string str; +public: + CModel() + { + cout << "CModel()" << endl; + a = 0; + str = " "; + } + CModel(int i, string s):a(i), str(s) + { + cout << "CModel(i, s)" << endl; + } + CModel(const CModel& m) + { + cout << "CModel(const CModel&)" << endl; + a = m.a; + str = m.str; + } + void print() + { + cout << "a: " << a << endl; + cout << "str: " << str << endl; + } + CModel& operator=(CModel& s) + { + a = s.a; + str = s.str; + cout << "using operator=" << endl; + } + + ~CModel() + { + cout << "using ~CModel()" << endl; + } +}; + +void showCModel(CModel& obj) +{ + obj.print(); +} +int main() +{ + CModel object(2, "xiangyu"); + CModel object2(3, "liuchang"); +// shared_ptr cp = make_shared(object); + vector Cvec; + Cvec.push_back(object); + Cvec.push_back(object2); + Cvec.push_back(object2); + Cvec.push_back(object); + cout << "size: " << Cvec.size() << "maxsize: " << Cvec.max_size() << endl; + for(auto it : Cvec) + { + it.print(); + } + +// showCModel(*cp); + return 0; +} + diff --git a/C++PrimerTest/C++PrimerTest.exe b/C++PrimerTest/C++PrimerTest.exe new file mode 100644 index 00000000..47c7b909 Binary files /dev/null and b/C++PrimerTest/C++PrimerTest.exe differ diff --git a/C++PrimerTest/MemoryCheckTest.cpp b/C++PrimerTest/MemoryCheckTest.cpp new file mode 100644 index 00000000..85248afb --- /dev/null +++ b/C++PrimerTest/MemoryCheckTest.cpp @@ -0,0 +1,23 @@ +#include + +using namespace std; +int main() +{ + +SYSTEM_INFO sysInfo; + + GetSystemInfo(&sysInfo); + + cout<<"机器属性:"< +#include +#include +#include +#include + +using namespace std; +//定义行为像值的类 +class HasPtr +{ +private: + string* ps; + int i; + +public: + HasPtr(const string& s = string(), int a = 0): + ps(new string(s)), i(a){} + HasPtr(const HasPtr& s): + ps(new string(*s.ps)), i(s.i){} + HasPtr& operator=(const HasPtr& p); + friend bool lessthan(const HasPtr& s1, const HasPtr& s2); + friend ostream& operator<<(ostream& os, const HasPtr& P); + friend void swap(HasPtr& , HasPtr&); + ~HasPtr() + { + delete ps; + } +}; +bool lessthan(const HasPtr& s1, const HasPtr& s2) +{ + if(s1.i < s2.i) + { + return true; + } + else + { + return false; + } +} +ostream& operator<<(ostream& os, const HasPtr& p) +{ + os << *p.ps << " " << p.i; +} +HasPtr& HasPtr::operator=(const HasPtr& p) +{ + i = p.i; + auto temp = new string(*p.ps); + delete ps; + ps = temp; + return *this; +} + +inline void swap(HasPtr& lsh, HasPtr& rsh) +{ + using std::swap; + swap(lsh.ps, rsh.ps); + swap(lsh.i, rsh.i); + cout << "using swap()" << endl; +} +//定义行为像指针的类 +class PHasPtr +{ +private: + int i; + string* ps; + size_t *use; //记录有多少个对象共享ps成员 +public: + PHasPtr(const string& s = string()): + i(0), ps(new string(s)), use(new size_t(1)){} + PHasPtr(const PHasPtr& p): + i(p.i), ps(p.ps), use(p.use) {*use++;} + PHasPtr& operator=(const PHasPtr& s); + ~PHasPtr() + { + *use--; + if(*use == 0) + { + delete ps; + delete use; + } + } +}; + +PHasPtr& PHasPtr::operator=(const PHasPtr& s) +{ + i = s.i; + if(--*use == 0) + { + delete ps; + delete use; + } + *(s,use)++; + ps = s.ps; + use = s.use; + return *this; +} + +class TreeNode +{ +private: + string value; + int count; + TreeNode *left; + size_t *leftnum; + TreeNode *right; + size_t *rightnum; +public: + TreeNode(): + value(nullptr), count(0), left(nullptr), right(nullptr),leftnum(new size_t(1)), rightnum(new size_t(1)){} + TreeNode(const TreeNode& t): + value(t.value), count(t.count), left(t.left), leftnum(t.leftnum), right(t.right), rightnum(t.rightnum) + { + *leftnum++; + *rightnum++; + } + ~TreeNode() + { + *leftnum--; + *rightnum--; + if(*leftnum == 0) + { + delete left; + delete leftnum; + } + if(*rightnum == 0) + { + delete right; + delete rightnum; + } + } +}; +int main() +{ + HasPtr a("xiangyu", 2); + HasPtr b("liuchang", 1); + HasPtr c("liuzheng", 3); + swap(a, b); + cout << "a: " << a << endl; + cout << "b: " << b << endl; + vector HasVec; + HasVec.push_back(a); + HasVec.push_back(b); + HasVec.push_back(c); + sort(HasVec.begin(), HasVec.end(), lessthan); + for(auto it : HasVec) + { + cout << it << endl; + } + + return 0; +} \ No newline at end of file diff --git a/C++PrimerTest/exercise13.4Message_Folder b/C++PrimerTest/exercise13.4Message_Folder new file mode 100644 index 00000000..10517660 Binary files /dev/null and b/C++PrimerTest/exercise13.4Message_Folder differ diff --git a/C++PrimerTest/exercise13.4Message_Folder.cpp b/C++PrimerTest/exercise13.4Message_Folder.cpp new file mode 100644 index 00000000..70b7303a --- /dev/null +++ b/C++PrimerTest/exercise13.4Message_Folder.cpp @@ -0,0 +1,106 @@ +#include "exercise13.4Message_Folder.h" + +/*DOWN*********************Message 类成员*************************************************/ +void Message::save(Folder& f) +{ + folders.insert(&f); + f.addMsg(this); +} +void Message::remove(Folder& f) +{ + folders.erase(&f); + f.remMsg(this); +} +//将本Message添加到指向m的Folder中 +void Message::add_to_Folders(const Message& m) +{ + for(auto it : m.folders) + { + it->addMsg(this); + } +} +void Message::remove_from_Folders() +{ + for(auto it : folders) + { + it->remMsg(this); + } +} +Message::Message(const Message& m) +{ + contents = m.contents; //拷贝内容文本 + folders = m.folders; //拷贝folders + add_to_Folders(m); //将本消息加到所有包含m的目录中 +} +Message::~Message() +{ + remove_from_Folders(); +} +Message& Message::operator=(const Message& m) +{ + remove_from_Folders(); + contents = m.contents; + folders = m.folders; + add_to_Folders(m); + return *this; +} +/*UP*********************************Message 类成员*************************************************/ +/*DOWN******************************Folder 类成员*************************************************/ +Folder::Folder(const Folder& f):Msg(f.Msg) +{ + for(auto it : Msg) + { + *it.folders.insert(this); + } +} +Folder::~Folder() +{ + for(auto it : Msg) + { + *it.folders.erase(this); + } +} +Folder& Folder::operator=(const Folder& f) +{ + for(auto it : Msg) + { + *it.folders.erase(this); + } + Msg = f.Msg; + for(auto it : Msg) + { + *it.folders.insert(&f); + } + +} +void Folder::addMsg(Message* s) +{ + Msg.insert(s); +} +void Folder::remMsg(Message* s) +{ + Msg.erase(s); +} +/*UP********************************Folder 类成员*************************************************/ +void swap(Message& m1, Message& m2) +{ + using std::swap; + swap(m1.contents, m2.contents); //交换文本内容 + //将m1从其目录中删除 + for(auto it : m1.folders) + { + it->remMsg(&m1); + } + //将m2从其目录中删除 + for(auto it : m2.folders) + { + it->remMsg(&m2); + } + m1.add_to_Folders(m2); + m2.add_to_Folders(m1); + swap(m1.folders, m2.folders); +} +int main() +{ + return 0; +} diff --git a/C++PrimerTest/exercise13.4Message_Folder.h b/C++PrimerTest/exercise13.4Message_Folder.h new file mode 100644 index 00000000..46386f37 --- /dev/null +++ b/C++PrimerTest/exercise13.4Message_Folder.h @@ -0,0 +1,42 @@ +#include +#include +#include + +using namespace std; +class Folder; +class Message +{ + friend class Folder; +private: + string contents; //实际文本消息 + set folders; //包含本Message的Foulder + //拷贝构造函数、拷贝赋值运算符和析构函数所使用的工具函数 + //将本Message添加到指定的Folder中 + void add_to_Folders(const Message&); + //从folders中的每个Folder中删除Message + void remove_from_Folders(); +public: + //folders被隐式初始化为空集合 + Message(const string& str = ""): + contents(str) {} //默认构造函数 + Message(const Message& ); //拷贝构造函数 + Message& operator=(const Message&); //拷贝复制运算符 + ~Message(); //析构函数 + //从给定Folder集合中添加/删除本Message + void save(Folder&); + void remove(Folder&); +}; +void swap(Message& , Message&); +class Folder +{ + friend class Message; +private: + set Msg; + +public: + Folder(const Folder& ); + Folder& operator=(const Folder&); + ~Folder(); + void addMsg(const Message*); //向此Folder中添加Message + void remMsg(const Message*); +}; \ No newline at end of file diff --git a/C++PrimerTest/exercise13.5StrVec b/C++PrimerTest/exercise13.5StrVec new file mode 100644 index 00000000..8729b602 Binary files /dev/null and b/C++PrimerTest/exercise13.5StrVec differ diff --git a/C++PrimerTest/exercise13.5StrVec.cpp b/C++PrimerTest/exercise13.5StrVec.cpp new file mode 100644 index 00000000..0c5a0add --- /dev/null +++ b/C++PrimerTest/exercise13.5StrVec.cpp @@ -0,0 +1,151 @@ +#include "exercise13.5StrVec.h" + +void StrVec::push_back(const string& s) +{ + chk_n_alloc(); //确保有足够空间容纳新元素 + alloc.construct(firstfree++, s); //构造新元素 +} + +pair StrVec::alloc_n_copy(const string* s1, const string* s2) +{ + auto data = alloc.allocate(s2-s1); + return {data, uninitialized_copy(s1, s2, data)}; +} + +/** +*free() +*首先销毁元素, 然后释放所有内存 +*/ +void StrVec::free() +{ + if(elements) + { + // for(auto p = firstfree; p!=elements;) + // alloc.destroy(--p); + for_each(elements, firstfree, [this](string& str){alloc.destroy(&str);}); //for_each这里传递的是迭代器指向的内容而不是指针 + alloc.deallocate(elements, cap-elements); + } +} +StrVec::StrVec(const StrVec& str) +{ + auto temp = alloc_n_copy(str.begin(), str.end()); + elements = temp.first; + firstfree = cap = temp.second; +} + +/** +*StrVec(initializer_list strlist) +*使用初始化列表的构造函数 +*/ +StrVec::StrVec(initializer_list strlist) +{ + auto newcapacity = strlist.size()*2; + auto data = alloc.allocate(newcapacity); + auto fisfree = uninitialized_copy(strlist.begin(), strlist.end(), data); + elements = data; + firstfree = fisfree; + cap = elements + newcapacity; +} +StrVec& StrVec::operator=(const StrVec& rhs) +{ + auto data = alloc_n_copy(rhs.begin(), rhs.end()); + free(); + elements = data.first; + firstfree = cap = data.second; + return *this; +} +StrVec::~StrVec() +{ + free(); +} +/** +*reallocate() +* +*/ +void StrVec::reallocate() +{ + auto newcapacity = size() ? size()*2:1; + auto newdata = alloc.allocate(newcapacity); + + auto dest = newdata; + auto elem = elements; + for(size_t i=0; i old_capacity) + { + auto newcapacity = n; + auto newdata = alloc.allocate(newcapacity); + + auto dest = newdata; + auto elem = elements; + for(size_t i=0; i capacity()) + { + + throw out_of_range("out of range!"); + return; + + } + + else if(n>size()) + { + auto count = n - size(); + uninitialized_fill_n(firstfree, count, ""); + } +} + +void StrVec::resize(size_t n, string str) +{ + if(n > capacity()) + { + + throw out_of_range("out of range!"); + return; + } + + else if(n>size()) + { + auto count = n - size(); + uninitialized_fill_n(firstfree, count, str); + } +} + +int main() +{ + StrVec test{"xianguy", "love", "liuchang"}; + test.push_back("xiangyu"); + test.push_back("liuchang"); + cout << "capacity: " << test.capacity() << endl; + cout << "size: " << test.size() << endl; + for(auto it=test.begin(); it !=test.end(); it++) + { + cout << *it << endl; + } + return 0; +} \ No newline at end of file diff --git a/C++PrimerTest/exercise13.5StrVec.h b/C++PrimerTest/exercise13.5StrVec.h new file mode 100644 index 00000000..b72e5cde --- /dev/null +++ b/C++PrimerTest/exercise13.5StrVec.h @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +//allocator alloc; +class StrVec +{ +private: + string* elements; //指向分配内存中的首元素 + string* firstfree; //指向最后一个实际元素之后的位置 + string* cap; //指向分配的内存末尾之后的位置 + allocator alloc; //分配元素 + void chk_n_alloc() + { + if(size() == capacity()) + reallocate(); + } + pair alloc_n_copy(const string*, const string*); + void free(); + void reallocate(); +public: + StrVec(): + elements(nullptr), firstfree(nullptr), cap(nullptr) {} + StrVec(initializer_list strlist); + StrVec(const StrVec&); //拷贝构造函数 + StrVec& operator=(const StrVec&); //赋值构造函数 + ~StrVec(); + void push_back(const string&); + size_t size() + {return (firstfree - elements);} + size_t capacity() + {return (cap - elements);} + string* begin() const + { + return elements; + } + string* end() const + { + return firstfree; + } + void reserve(size_t n); + void resize(size_t n); + void resize(size_t n, string str); + + +}; \ No newline at end of file diff --git a/C++PrimerTest/exercise15-2oop.cpp b/C++PrimerTest/exercise15-2oop.cpp new file mode 100644 index 00000000..c0d245f3 --- /dev/null +++ b/C++PrimerTest/exercise15-2oop.cpp @@ -0,0 +1,117 @@ +#include "exercise15-2oop.h" + +double Bulk_quote::net_price(size_t n) const +{ + if(n >= quantity) + { + return n*(1-discount)*price; + } + else + { + return n*price; + } +} + +/** +*Quote类的拷贝构造函数 +*/ +Quote::Quote(const Quote& q) +{ + bookNo = q.bookNo; + price = q.price; + cout << "Base_Class Copy Constructor!!!" < +#include +#include +#include +#include +using namespace std; + +class Quote +{ +private: + string bookNo; //书籍的ISBN编号 + +protected: + double price = 0.0; //代表普通状态下不打折的价格 +public: + Quote() = default; + Quote(const string isbn, double pri): + bookNo(isbn), price(pri) {} + //拷贝构造函数 + Quote(const Quote& d); + //拷贝复制运算符 + Quote& operator=(const Quote&); + + //该虚函数返回当前对象的一份动态分配的拷贝 + virtual Quote* clone() const & {return new Quote(*this);} + virtual Quote* clone() && {return new Quote(std::move(*this));} + //返回给定数量的书籍的销售总额 + //派生类负责改写并使用不同的折扣算法 + virtual double net_price(size_t n) const + { + return n*price; + } + + string isbn() const + { + return bookNo; + } + virtual ~Quote(); //对析构函数进行动态绑定 +}; + + +// class Bulk_quote: public Disc_quote +// { +// private: +// size_t min_qty = 0; //获得折扣的最小购买数量 +// double discount = 0.0; //折扣 +// public: +// Bulk_quote() = default; +// Bulk_quote(const string isbn, double pri, size_t min_num, double disc): +// Quote(isbn, pri), min_qty(min_num), discount(disc) {} + +// //重写此函数以覆盖基类中的此函数 +// double net_price(size_t n) const override; +// ~Bulk_quote(){} +// }; + +class Disc_quote:public Quote +{ +// using Quote::Quote; +protected: + size_t quantity = 0; + double discount = 0.0; +public: + Disc_quote() = default; + Disc_quote(const string isbn, double pri, size_t qty, double disc): + Quote(isbn, pri), quantity(qty), discount(disc){} + //拷贝构造函数 + Disc_quote(const Disc_quote&); + //拷贝复制运算符 + Disc_quote& operator=(const Disc_quote&); + + double net_price(size_t n) const = 0; //纯虚函数,函数定义必须放在外面 + ~Disc_quote(); +}; + +class Bulk_quote: public Disc_quote +{ +// using Disc_quote::Disc_quote; +public: + Bulk_quote() =default; + Bulk_quote(const string isbn, double pri, size_t min_num, double disc): + Disc_quote(isbn, pri, min_num, disc) {} + //拷贝构造函数 + Bulk_quote(const Bulk_quote&); + Bulk_quote& operator=(const Bulk_quote&); + + Bulk_quote* clone() const & {return new Bulk_quote(*this);} + Bulk_quote* clone() && {return new Bulk_quote(std::move(*this));} + //重写此函数以覆盖基类中的此函数 + double net_price(size_t n) const override; + ~Bulk_quote(); +}; + +class Basket +{ +private: + + //排序规则,用于multiset中的排序规则 + static bool compare(const shared_ptr &lsh, const shared_ptr &rsh) + { + return (lsh->isbn() < rsh->isbn()); + } + multiset, decltype(compare)*> items{compare}; +public: + //Basket类使用合成的默认构造函数和拷贝控制成员 + + // void add_item(const shared_ptr &sale) + // { + // items.insert(sale); + // } + void add_item(const Quote& sale) //拷贝给定对象 + { + items.insert(shared_ptr (sale.clone())); + } + void add_item(Quote&& sale) + { + items.insert(shared_ptr (sale.clone())); + } + //打印每本书的总价和购物篮中的所有书的总价 + double total_receipt(ostream &os) const; + +}; \ No newline at end of file diff --git a/TextQuery/TextQuery.cpp b/TextQuery/TextQuery.cpp new file mode 100644 index 00000000..33c27df9 --- /dev/null +++ b/TextQuery/TextQuery.cpp @@ -0,0 +1,79 @@ +#include "TextQuery.h" + + +TextQuery::TextQuery(ifstream& infile):file(new vector) +{ + string text; + while(getline(infile, text)) //对文件中的每一行 + { + file->push_back(text); //保存一行文本 + int n = file->size()-1; //记录行号 + istringstream line(text); //分配一个字符串流 + string word; + while(line >> word) //提取一行字符串中的没一个单词 + { + auto &lines = wm[word]; //lines 的类型为mapped_type是一个shared_ptr,指向set + if(!lines) //如果word在wm中不存在,则lines是一个空指针 + { + lines.reset(new set); //如果不存在,为lines分配一个set空间,并用reset使lines指向新分配的set + } + lines->insert(n); //将行号插入到对应word的set中 + } + } +} +QueryResult TextQuery::query(const string& word) const +{ + static shared_ptr> nodata(new set); //如果未找到word,就返回这个指针 + auto loc = wm.find(word); + if(loc == wm.end()) + { + return QueryResult(word, file, nodata); //未找到 + } + else + { + return QueryResult(word, file, loc->second); + } + + +} +QueryResult::QueryResult(const QueryResult& s) +{ + sought = s.sought; + file = make_shared>(); + copy(s.file->begin(), s.file->end(), file->begin()); + lines = make_shared>(); + copy(s.lines->begin(), s.lines->end(), lines->begin()); +} +ostream& print(ostream &os, const QueryResult& qr) +{ + os << qr.sought << " occurs " << qr.lines->size() + << "times:" << endl; + for(auto it : *qr.lines) + { + cout << "\t (line " << it+1 << ") "; + cout << *(qr.file->begin() + it) <> s) || s == "q") + break; + else + print(cout, tq.query(s)) << endl; + } +} +int main() +{ + + ifstream infile("TextQuery.txt"); + runQueries(infile); + return 0; +} \ No newline at end of file diff --git a/TextQuery/TextQuery.exe b/TextQuery/TextQuery.exe new file mode 100644 index 00000000..f468b943 Binary files /dev/null and b/TextQuery/TextQuery.exe differ diff --git a/TextQuery/TextQuery.h b/TextQuery/TextQuery.h new file mode 100644 index 00000000..a075f608 --- /dev/null +++ b/TextQuery/TextQuery.h @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; +using line_no = vector::size_type; +class QueryResult; +class TextQuery +{ +public: + + TextQuery(ifstream &infile); + QueryResult query(const string &word) const; +private: + //定义输入文件的智能指针 + shared_ptr> file; + //每个单词到它所在行号的集合的映射 + map>> wm; +}; +class QueryResult +{ +private: + string sought; //查询的单词 + shared_ptr> file; //指向查询文件的指针 + shared_ptr> lines; //指向行号的指针 +public: + QueryResult(string s, shared_ptr> p, shared_ptr> l):sought(s), file(p), lines(l){} + QueryResult(const QueryResult& s); + friend ostream& print(ostream&, const QueryResult&); + + +}; \ No newline at end of file diff --git a/TextQuery/TextQuery.txt b/TextQuery/TextQuery.txt new file mode 100644 index 00000000..a87414d4 --- /dev/null +++ b/TextQuery/TextQuery.txt @@ -0,0 +1,14 @@ +When love beckons to you follow him +though his ways are +hard and steep And when +his wings enfold you +yield to him though the sword +hidden among his +pinions may wound you +And when he speaks to you +believe in him +though his voice +may shatter your dreams as +the north wind lays +waste the garden. + diff --git a/leetcode/.gitignore b/leetcode/.gitignore new file mode 100644 index 00000000..1ed19897 --- /dev/null +++ b/leetcode/.gitignore @@ -0,0 +1,6 @@ +*.log +*.toc +*.aux +*.idx +*.out +*.synctex.gz diff --git a/leetcode/C++/README.md b/leetcode/C++/README.md new file mode 100644 index 00000000..6894ad25 --- /dev/null +++ b/leetcode/C++/README.md @@ -0,0 +1,5 @@ +#C++版 +----------------- +**下载**:LeetCode题解(C++版).pdf + +书的内容与Java版一摸一样,不过代码是用C++写的。本书的代码使用 C++ 11 标准。 diff --git a/leetcode/C++/abstract.tex b/leetcode/C++/abstract.tex new file mode 100644 index 00000000..8747e624 --- /dev/null +++ b/leetcode/C++/abstract.tex @@ -0,0 +1,27 @@ +\subsubsection{内容简介} +本书的目标读者是准备去北美找工作的码农,也适用于在国内找工作的码农,以及刚接触ACM算法竞赛的新手。 + +本书包含了 LeetCode Online Judge(\myurl{http://leetcode.com/onlinejudge})所有题目的答案, +所有代码经过精心编写,编码规范良好,适合读者反复揣摩,模仿,甚至在纸上默写。 + +全书的代码,使用C++ 11的编写,并在 LeetCode Online Judge 上测试通过。本书中的代码规范,跟在公司中的工程规范略有不同,为了使代码短(方便迅速实现): + +\begindot +\item 所有代码都是单一文件。这是因为一般OJ网站,提交代码的时候只有一个文本框,如果还是 +按照标准做法,比如分为头文件.h和源代码.cpp,无法在网站上提交; + +\item Shorter is better。能递归则一定不用栈;能用STL则一定不自己实现。 + +\item 不提倡防御式编程。不需要检查malloc()/new 返回的指针是否为nullptr;不需要检查内部函数入口参数的有效性。 +\myenddot + +本手册假定读者已经学过《数据结构》\footnote{《数据结构》,严蔚敏等著,清华大学出版社, +\myurl{http://book.douban.com/subject/2024655/}}, +《算法》\footnote{《Algorithms》,Robert Sedgewick, Addison-Wesley Professional, \myurl{http://book.douban.com/subject/4854123/}} +这两门课,熟练掌握C++或Java。 + +\subsubsection{GitHub地址} +本书是开源的,GitHub地址:\myurl{https://github.com/soulmachine/leetcode} + +\subsubsection{北美求职微博群} +我和我的小伙伴们在这里:\myurl{http://q.weibo.com/1312378} diff --git a/leetcode/C++/chapBFS.tex b/leetcode/C++/chapBFS.tex new file mode 100644 index 00000000..9ad2297d --- /dev/null +++ b/leetcode/C++/chapBFS.tex @@ -0,0 +1,589 @@ +\chapter{广度优先搜索} +当题目看不出任何规律,既不能用分治,贪心,也不能用动规时,这时候万能方法——搜索, +就派上用场了。搜索分为广搜和深搜,广搜里面又有普通广搜,双向广搜,A*搜索等。 +深搜里面又有普通深搜,回溯法等。 + +广搜和深搜非常类似(除了在扩展节点这部分不一样),二者有相同的框架,如何表示状态? +如何扩展状态?如何判重?尤其是判重,解决了这个问题,基本上整个问题就解决了。 + + +\section{Word Ladder} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:word-ladder} + + +\subsubsection{描述} +Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that: +\begindot +\item Only one letter can be changed at a time +\item Each intermediate word must exist in the dictionary +\myenddot + +For example, Given: + +\begin{Code} +start = "hit" +end = "cog" +dict = ["hot","dot","dog","lot","log"] +\end{Code} +As one shortest transformation is \code{"hit" -> "hot" -> "dot" -> "dog" -> "cog"}, return its length $5$. + +Note: +\begindot +\item Return 0 if there is no such transformation sequence. +\item All words have the same length. +\item All words contain only lowercase alphabetic characters. +\myenddot + + +\subsubsection{分析} + + +\subsubsection{代码} +\begin{Code} +//LeetCode, Word Ladder +// 时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + int ladderLength(const string& start, const string &end, + const unordered_set &dict) { + queue current, next; // 当前层,下一层 + unordered_set visited; // 判重 + + int level = 0; // 层次 + bool found = false; + + auto state_is_target = [&](const string &s) {return s == end;}; + auto state_extend = [&](const string &s) { + vector result; + + for (size_t i = 0; i < s.size(); ++i) { + string new_word(s); + for (char c = 'a'; c <= 'z'; c++) { + if (c == new_word[i]) continue; + + swap(c, new_word[i]); + + if ((dict.count(new_word) > 0 || new_word == end) && + !visited.count(new_word)) { + result.push_back(new_word); + visited.insert(new_word); + } + swap(c, new_word[i]); // 恢复该单词 + } + } + + return result; + }; + + current.push(start); + while (!current.empty() && !found) { + ++level; + while (!current.empty() && !found) { + const string str = current.front(); + current.pop(); + + const auto& new_states = state_extend(str); + for (const auto& state : new_states) { + next.push(state); + if (state_is_target(state)) { + found = true; //找到了 + break; + } + } + } + swap(next, current); + } + if (found) return level + 1; + else return 0; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item Word Ladder II,见 \S \ref{sec:word-ladder-ii} +\myenddot + + +\section{Word Ladder II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:word-ladder-ii} + + +\subsubsection{描述} +Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that: +\begindot +\item Only one letter can be changed at a time +\item Each intermediate word must exist in the dictionary +\myenddot + +For example, Given: +\begin{Code} +start = "hit" +end = "cog" +dict = ["hot","dot","dog","lot","log"] +\end{Code} +Return +\begin{Code} +[ + ["hit","hot","dot","dog","cog"], + ["hit","hot","lot","log","cog"] +] +\end{Code} + +Note: +\begindot +\item All words have the same length. +\item All words contain only lowercase alphabetic characters. +\myenddot + + +\subsubsection{分析} +跟 Word Ladder比,这题是求路径本身,不是路径长度,也是BFS,略微麻烦点。 + +这题跟普通的广搜有很大的不同,就是要输出所有路径,因此在记录前驱和判重地方与普通广搜略有不同。 + + +\subsubsection{代码} + +\begin{Code} +//LeetCode, Word Ladder II +// 时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + vector > findLadders(string start, string end, + const unordered_set &dict) { + unordered_set current, next; // 当前层,下一层,用集合是为了去重 + unordered_set visited; // 判重 + unordered_map > father; // 树 + + bool found = false; + + auto state_is_target = [&](const string &s) {return s == end;}; + auto state_extend = [&](const string &s) { + unordered_set result; + + for (size_t i = 0; i < s.size(); ++i) { + string new_word(s); + for (char c = 'a'; c <= 'z'; c++) { + if (c == new_word[i]) continue; + + swap(c, new_word[i]); + + if ((dict.count(new_word) > 0 || new_word == end) && + !visited.count(new_word)) { + result.insert(new_word); + } + swap(c, new_word[i]); // 恢复该单词 + } + } + + return result; + }; + + current.insert(start); + while (!current.empty() && !found) { + // 先将本层全部置为已访问,防止同层之间互相指向 + for (const auto& word : current) + visited.insert(word); + for (const auto& word : current) { + const auto new_states = state_extend(word); + for (const auto &state : new_states) { + if (state_is_target(state)) found = true; + next.insert(state); + father[state].push_back(word); + // visited.insert(state); // 移动到最上面了 + } + } + + current.clear(); + swap(current, next); + } + vector > result; + if (found) { + vector path; + gen_path(father, path, start, end, result); + } + return result; + } +private: + void gen_path(unordered_map > &father, + vector &path, const string &start, const string &word, + vector > &result) { + path.push_back(word); + if (word == start) { + result.push_back(path); + reverse(result.back().begin(), result.back().end()); + } else { + for (const auto& f : father[word]) { + gen_path(father, path, start, f, result); + } + } + path.pop_back(); + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item Word Ladder,见 \S \ref{sec:word-ladder} +\myenddot + + +\section{Surrounded Regions} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:surrounded-regions} + + +\subsubsection{描述} +Given a 2D board containing \fn{'X'} and \fn{'O'}, capture all regions surrounded by \fn{'X'}. + +A region is captured by flipping all \fn{'O'}s into \fn{'X'}s in that surrounded region . + +For example, +\begin{Code} +X X X X +X O O X +X X O X +X O X X +\end{Code} + +After running your function, the board should be: +\begin{Code} +X X X X +X X X X +X X X X +X O X X +\end{Code} + + +\subsubsection{分析} +广搜。从上下左右四个边界往里走,凡是能碰到的\fn{'O'},都是跟边界接壤的,应该保留。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Surrounded Regions +// BFS,时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + void solve(vector> &board) { + if (board.empty()) return; + + const int m = board.size(); + const int n = board[0].size(); + for (int i = 0; i < n; i++) { + bfs(board, 0, i); + bfs(board, m - 1, i); + } + for (int j = 1; j < m - 1; j++) { + bfs(board, j, 0); + bfs(board, j, n - 1); + } + for (int i = 0; i < m; i++) + for (int j = 0; j < n; j++) + if (board[i][j] == 'O') + board[i][j] = 'X'; + else if (board[i][j] == '+') + board[i][j] = 'O'; + } +private: + void bfs(vector> &board, int i, int j) { + typedef pair state_t; + queue q; + const int m = board.size(); + const int n = board[0].size(); + + auto is_valid = [&](const state_t &s) { + const int x = s.first; + const int y = s.second; + if (x < 0 || x >= m || y < 0 || y >= n || board[x][y] != 'O') + return false; + return true; + }; + + auto state_extend = [&](const state_t &s) { + vector result; + const int x = s.first; + const int y = s.second; + // 上下左右 + const state_t new_states[4] = {{x-1,y}, {x+1,y}, + {x,y-1}, {x,y+1}}; + for (int k = 0; k < 4; ++k) { + if (is_valid(new_states[k])) { + // 既有标记功能又有去重功能 + board[new_states[k].first][new_states[k].second] = '+'; + result.push_back(new_states[k]); + } + } + + return result; + }; + + state_t start = { i, j }; + if (is_valid(start)) { + board[i][j] = '+'; + q.push(start); + } + while (!q.empty()) { + auto cur = q.front(); + q.pop(); + auto new_states = state_extend(cur); + for (auto s : new_states) q.push(s); + } + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item 无 +\myenddot + + +\section{小结} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:bfs-template} + + +\subsection{适用场景} + +\textbf{输入数据}:没什么特征,不像深搜,需要有“递归”的性质。如果是树或者图,概率更大。 + +\textbf{状态转换图}:树或者图。 + +\textbf{求解目标}:求最短。 + + +\subsection{思考的步骤} +\begin{enumerate} +\item 是求路径长度,还是路径本身(或动作序列)? + \begin{enumerate} + \item 如果是求路径长度,则状态里面要存路径长度(或双队列+一个全局变量) + \item 如果是求路径本身或动作序列 + \begin{enumerate} + \item 要用一棵树存储宽搜过程中的路径 + \item 是否可以预估状态个数的上限?能够预估状态总数,则开一个大数组,用树的双亲表示法;如果不能预估状态总数,则要使用一棵通用的树。这一步也是第4步的必要不充分条件。 + \end{enumerate} + \end{enumerate} + +\item 如何表示状态?即一个状态需要存储哪些些必要的数据,才能够完整提供如何扩展到下一步状态的所有信息。一般记录当前位置或整体局面。 + +\item 如何扩展状态?这一步跟第2步相关。状态里记录的数据不同,扩展方法就不同。对于固定不变的数据结构(一般题目直接给出,作为输入数据),如二叉树,图等,扩展方法很简单,直接往下一层走,对于隐式图,要先在第1步里想清楚状态所带的数据,想清楚了这点,那如何扩展就很简单了。 + +\item 关于判重,状态是否存在完美哈希方案?即将状态一一映射到整数,互相之间不会冲突。 + \begin{enumerate} + \item 如果不存在,则需要使用通用的哈希表(自己实现或用标准库,例如\fn{unordered_set})来判重;自己实现哈希表的话,如果能够预估状态个数的上限,则可以开两个数组,head和next,表示哈希表,参考第 \S \ref{subsec:eightDigits}节方案2。 + \item 如果存在,则可以开一个大布尔数组,作为哈希表来判重,且此时可以精确计算出状态总数,而不仅仅是预估上限。 + \end{enumerate} + +\item 目标状态是否已知?如果题目已经给出了目标状态,可以带来很大便利,这时候可以从起始状态出发,正向广搜;也可以从目标状态出发,逆向广搜;也可以同时出发,双向广搜。 +\end{enumerate} + + +\subsection{代码模板} +广搜需要一个队列,用于一层一层扩展,一个hashset,用于判重,一棵树(只求长度时不需要),用于存储整棵树。 + +对于队列,可以用\fn{queue},也可以把\fn{vector}当做队列使用。当求长度时,有两种做法: +\begin{enumerate} +\item 只用一个队列,但在状态结构体\fn{state_t}里增加一个整数字段\fn{step},表示走到当前状态用了多少步,当碰到目标状态,直接输出\fn{step}即可。这个方案,可以很方便的变成A*算法,把队列换成优先队列即可。 +\item 用两个队列,\fn{current, next},分别表示当前层次和下一层,另设一个全局整数\fn{level},表示层数(也即路径长度),当碰到目标状态,输出\fn{level}即可。这个方案,状态可以少一个字段,节省内存。 +\end{enumerate} + +对于hashset,如果有完美哈希方案,用布尔数组(\fn{bool visited[STATE_MAX]}或\fn{vector visited(STATE_MAX, false)})来表示;如果没有,可以用STL里的\fn{set}或\fn{unordered_set}。 + +对于树,如果用STL,可以用\fn{unordered_map father}表示一颗树,代码非常简洁。如果能够预估状态总数的上限(设为STATE_MAX),可以用数组(\fn{state_t nodes[STATE_MAX]}),即树的双亲表示法来表示树,效率更高,当然,需要写更多代码。 + + +\subsubsection{双队列的写法} +\begin{Codex}[label=bfs_template1.cpp] +/** 状态 */ +struct state_t { + int data1; /** 状态的数据,可以有多个字段. */ + int data2; /** 状态的数据,可以有多个字段. */ + // dataN; /** 其他字段 */ + int action; /** 由父状态移动到本状态的动作,求动作序列时需要. */ + int count; /** 所花费的步骤数(也即路径长度-1),求路径长度时需要; + 不过,采用双队列时不需要本字段,只需全局设一个整数 */ + bool operator==(const state_t &other) const { + return true; // 根据具体问题实现 + } +}; + +// 定义hash函数 + +// 方法1:模板特化,当hash函数只需要状态本身,不需要其他数据时,用这个方法比较简洁 +namespace std { +template<> struct hash { + size_t operator()(const state_t & x) const { + return 0; // 根据具体问题实现 + } +}; +} + +// 方法2:函数对象,如果hash函数需要运行时数据,则用这种方法 +class Hasher { +public: + Hasher(int _m) : m(_m) {}; + size_t operator()(const state_t &s) const { + return 0; // 根据具体问题实现 + } +private: + int m; // 存放外面传入的数据 +}; + + +/** + * @brief 反向生成路径. + * @param[in] father 树 + * @param[in] target 目标节点 + * @return 从起点到target的路径 + */ +template +vector gen_path(const unordered_map &father, + const state_t &target) { + vector path; + path.push_back(target); + + for (state_t cur = target; father.find(cur) != father.end(); + cur = father.at(cur)) + path.push_back(cur); + + reverse(path.begin(), path.end()); + + return path; +} + +/** + * @brief 广搜. + * @param[in] state_t 状态,如整数,字符串,一维数组等 + * @param[in] start 起点 + * @param[in] grid 输入数据 + * @return 从起点到目标状态的一条最短路径 + */ +template +vector bfs(const state_t &start, const vector> &grid) { + queue next, current; // 当前层,下一层 + unordered_set visited; // 判重 + unordered_map father; // 树 + + int level = 0; // 层次 + bool found = false; // 是否找到目标 + state_t target; // 符合条件的目标状态 + + // 判断当前状态是否为所求目标 + auto state_is_target = [&](const state_t &s) {return true; }; + // 扩展当前状态 + auto state_extend = [&](const state_t &s) { + vector result; + // ... + return result; + }; + + current.push(start); + visited.insert(start); + while (!current.empty() && !found) { + ++level; + while (!current.empty() && !found) { + const state_t state = current.front(); + current.pop(); + vector new_states = state_extend(state); + for (auto iter = new_states.cbegin(); + iter != new_states.cend() && ! found; ++iter) { + const state_t new_state(*iter); + + if (state_is_target(new_state)) { + found = true; //找到了 + target = new_state; + father[new_state] = state; + break; + } + + next.push(new_state); + // visited.insert(new_state); 必须放到 state_extend()里 + father[new_state] = state; + } + } + swap(next, current); //!!! 交换两个队列 + } + + if (found) { + return gen_path(father, target); + //return level + 1; + } else { + return vector(); + //return 0; + } +} +\end{Codex} + + +\subsubsection{只用一个队列的写法} +双队列的写法,当求路径长度时,不需要在状态里设置一个\fn{count}字段记录路径长度,只需全局设置一个整数\fn{level},比较节省内存;只用一个队列的写法,当求路径长度时,需要在状态里设置一个\fn{count}字段,不过,这种写法有一个好处 —— 可以很容易的变为A*算法,把\fn{queue}替换为\fn{priority_queue}即可。 + +\begin{Codex}[label=bfs_template2.cpp] +// 与模板1相同的部分,不再重复 +// ... + +/** + * @brief 广搜. + * @param[in] state_t 状态,如整数,字符串,一维数组等 + * @param[in] start 起点 + * @param[in] grid 输入数据 + * @return 从起点到目标状态的一条最短路径 + */ +template +vector bfs(state_t &start, const vector> &grid) { + queue q; // 队列 + unordered_set visited; // 判重 + unordered_map father; // 树 + + int level = 0; // 层次 + bool found = false; // 是否找到目标 + state_t target; // 符合条件的目标状态 + + // 判断当前状态是否为所求目标 + auto state_is_target = [&](const state_t &s) {return true; }; + // 扩展当前状态 + auto state_extend = [&](const state_t &s) { + vector result; + // ... + return result; + }; + + start.count = 0; + q.push(start); + visited.insert(start); + while (!q.empty() && !found) { + const state_t state = q.front(); + q.pop(); + vector new_states = state_extend(state); + for (auto iter = new_states.cbegin(); + iter != new_states.cend() && ! found; ++iter) { + const state_t new_state(*iter); + + if (state_is_target(new_state)) { + found = true; //找到了 + target = new_state; + father[new_state] = state; + break; + } + + q.push(new_state); + // visited.insert(new_state); 必须放到 state_extend()里 + father[new_state] = state; + } + } + + if (found) { + return gen_path(father, target); + //return level + 1; + } else { + return vector(); + //return 0; + } +} +\end{Codex} diff --git a/leetcode/C++/chapBruteforce.tex b/leetcode/C++/chapBruteforce.tex new file mode 100644 index 00000000..08014864 --- /dev/null +++ b/leetcode/C++/chapBruteforce.tex @@ -0,0 +1,789 @@ +\chapter{暴力枚举法} + + +\section{Subsets} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:subsets} + + +\subsubsection{描述} +Given a set of distinct integers, $S$, return all possible subsets. + +Note: +\begindot +\item Elements in a subset must be in non-descending order. +\item The solution set must not contain duplicate subsets. +\myenddot + +For example, If \code{S = [1,2,3]}, a solution is: +\begin{Code} +[ + [3], + [1], + [2], + [1,2,3], + [1,3], + [2,3], + [1,2], + [] +] +\end{Code} + + +\subsection{递归} + + +\subsubsection{增量构造法} +每个元素,都有两种选择,选或者不选。 + +\begin{Code} +// LeetCode, Subsets +// 增量构造法,深搜,时间复杂度O(2^n),空间复杂度O(n) +class Solution { +public: + vector > subsets(vector &S) { + sort(S.begin(), S.end()); // 输出要求有序 + vector > result; + vector path; + subsets(S, path, 0, result); + return result; + } + +private: + static void subsets(const vector &S, vector &path, int step, + vector > &result) { + if (step == S.size()) { + result.push_back(path); + return; + } + // 不选S[step] + subsets(S, path, step + 1, result); + // 选S[step] + path.push_back(S[step]); + subsets(S, path, step + 1, result); + path.pop_back(); + } +}; +\end{Code} + + +\subsubsection{位向量法} +开一个位向量\fn{bool selected[n]},每个元素可以选或者不选。 + +\begin{Code} +// LeetCode, Subsets +// 位向量法,深搜,时间复杂度O(2^n),空间复杂度O(n) +class Solution { +public: + vector > subsets(vector &S) { + sort(S.begin(), S.end()); // 输出要求有序 + + vector > result; + vector selected(S.size(), false); + subsets(S, selected, 0, result); + return result; + } + +private: + static void subsets(const vector &S, vector &selected, int step, + vector > &result) { + if (step == S.size()) { + vector subset; + for (int i = 0; i < S.size(); i++) { + if (selected[i]) subset.push_back(S[i]); + } + result.push_back(subset); + return; + } + // 不选S[step] + selected[step] = false; + subsets(S, selected, step + 1, result); + // 选S[step] + selected[step] = true; + subsets(S, selected, step + 1, result); + } +}; +\end{Code} + + +\subsection{迭代} + + +\subsubsection{增量构造法} +\begin{Code} +// LeetCode, Subsets +// 迭代版,时间复杂度O(2^n),空间复杂度O(1) +class Solution { +public: + vector > subsets(vector &S) { + sort(S.begin(), S.end()); // 输出要求有序 + vector > result(1); + for (auto elem : S) { + result.reserve(result.size() * 2); + auto half = result.begin() + result.size(); + copy(result.begin(), half, back_inserter(result)); + for_each(half, result.end(), [&elem](decltype(result[0]) &e){ + e.push_back(elem); + }); + } + return result; + } +}; +\end{Code} + + +\subsubsection{二进制法} +本方法的前提是:集合的元素不超过int位数。用一个int整数表示位向量,第$i$位为1,则表示选择$S[i]$,为0则不选择。例如\fn{S=\{A,B,C,D\}},则\fn{0110=6}表示子集\fn{\{B,C\}}。 + +这种方法最巧妙。因为它不仅能生成子集,还能方便的表示集合的并、交、差等集合运算。设两个集合的位向量分别为$B_1$和$B_2$,则$B_1\cup B_2, B_1 \cap B_2, B_1 \triangle B_2$分别对应集合的并、交、对称差。 + +二进制法,也可以看做是位向量法,只不过更加优化。 + +\begin{Code} +// LeetCode, Subsets +// 二进制法,时间复杂度O(2^n),空间复杂度O(1) +class Solution { +public: + vector > subsets(vector &S) { + sort(S.begin(), S.end()); // 输出要求有序 + vector > result; + const size_t n = S.size(); + vector v; + + for (size_t i = 0; i < 1 << n; i++) { + for (size_t j = 0; j < n; j++) { + if (i & 1 << j) v.push_back(S[j]); + } + result.push_back(v); + v.clear(); + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Subsets II,见 \S \ref{sec:subsets-ii} +\myenddot + + +\section{Subsets II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:subsets-ii} + + +\subsubsection{描述} +Given a collection of integers that might contain duplicates, $S$, return all possible subsets. + +Note: + +Elements in a subset must be in non-descending order. +The solution set must not contain duplicate subsets. +For example, +If \fn{S = [1,2,2]}, a solution is: +\begin{Code} +[ + [2], + [1], + [1,2,2], + [2,2], + [1,2], + [] +] +\end{Code} + + +\subsubsection{分析} +这题有重复元素,但本质上,跟上一题很类似,上一题中元素没有重复,相当于每个元素只能选0或1次,这里扩充到了每个元素可以选0到若干次而已。 + + +\subsection{递归} + + +\subsubsection{增量构造法} +\begin{Code} +// LeetCode, Subsets II +// 增量构造法,版本1,时间复杂度O(2^n),空间复杂度O(n) +class Solution { +public: + vector > subsetsWithDup(vector &S) { + sort(S.begin(), S.end()); // 必须排序 + + vector > result; + vector path; + + dfs(S, S.begin(), path, result); + return result; + } + +private: + static void dfs(const vector &S, vector::iterator start, + vector &path, vector > &result) { + result.push_back(path); + + for (auto i = start; i < S.end(); i++) { + if (i != start && *i == *(i-1)) continue; + path.push_back(*i); + dfs(S, i + 1, path, result); + path.pop_back(); + } + } +}; +\end{Code} + +\begin{Code} +// LeetCode, Subsets II +// 增量构造法,版本2,时间复杂度O(2^n),空间复杂度O(n) +class Solution { +public: + vector > subsetsWithDup(vector &S) { + vector > result; + sort(S.begin(), S.end()); // 必须排序 + + unordered_map count_map; // 记录每个元素的出现次数 + for_each(S.begin(), S.end(), [&count_map](int e) { + if (count_map.find(e) != count_map.end()) + count_map[e]++; + else + count_map[e] = 1; + }); + + // 将map里的pair拷贝到一个vector里 + vector > elems; + for_each(count_map.begin(), count_map.end(), + [&elems](const pair &e) { + elems.push_back(e); + }); + sort(elems.begin(), elems.end()); + vector path; // 中间结果 + + subsets(elems, 0, path, result); + return result; + } + +private: + static void subsets(const vector > &elems, + size_t step, vector &path, vector > &result) { + if (step == elems.size()) { + result.push_back(path); + return; + } + + for (int i = 0; i <= elems[step].second; i++) { + for (int j = 0; j < i; ++j) { + path.push_back(elems[step].first); + } + subsets(elems, step + 1, path, result); + for (int j = 0; j < i; ++j) { + path.pop_back(); + } + } + } +}; +\end{Code} + + +\subsubsection{位向量法} +\begin{Code} +// LeetCode, Subsets II +// 位向量法,时间复杂度O(2^n),空间复杂度O(n) +class Solution { +public: + vector > subsetsWithDup(vector &S) { + vector > result; // 必须排序 + sort(S.begin(), S.end()); + vector count(S.back() - S.front() + 1, 0); + // 计算所有元素的个数 + for (auto i : S) { + count[i - S[0]]++; + } + + // 每个元素选择了多少个 + vector selected(S.back() - S.front() + 1, -1); + + subsets(S, count, selected, 0, result); + return result; + } + +private: + static void subsets(const vector &S, vector &count, + vector &selected, size_t step, vector > &result) { + if (step == count.size()) { + vector subset; + for(size_t i = 0; i < selected.size(); i++) { + for (int j = 0; j < selected[i]; j++) { + subset.push_back(i+S[0]); + } + } + result.push_back(subset); + return; + } + + for (int i = 0; i <= count[step]; i++) { + selected[step] = i; + subsets(S, count, selected, step + 1, result); + } + } +}; +\end{Code} + + +\subsection{迭代} + + +\subsubsection{增量构造法} +\begin{Code} +// LeetCode, Subsets II +// 增量构造法 +// 时间复杂度O(2^n),空间复杂度O(1) +class Solution { +public: + vector > subsetsWithDup(vector &S) { + sort(S.begin(), S.end()); // 必须排序 + vector > result(1); + + size_t previous_size = 0; + for (size_t i = 0; i < S.size(); ++i) { + const size_t size = result.size(); + for (size_t j = 0; j < size; ++j) { + if (i == 0 || S[i] != S[i-1] || j >= previous_size) { + result.push_back(result[j]); + result.back().push_back(S[i]); + } + } + previous_size = size; + } + return result; + } +}; +\end{Code} + + +\subsubsection{二进制法} +\begin{Code} +// LeetCode, Subsets II +// 二进制法,时间复杂度O(2^n),空间复杂度O(1) +class Solution { +public: + vector > subsetsWithDup(vector &S) { + sort(S.begin(), S.end()); // 必须排序 + // 用 set 去重,不能用 unordered_set,因为输出要求有序 + set > result; + const size_t n = S.size(); + vector v; + + for (size_t i = 0; i < 1U << n; ++i) { + for (size_t j = 0; j < n; ++j) { + if (i & 1 << j) + v.push_back(S[j]); + } + result.insert(v); + v.clear(); + } + vector > real_result; + copy(result.begin(), result.end(), back_inserter(real_result)); + return real_result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Subsets,见 \S \ref{sec:subsets} +\myenddot + + +\section{Permutations} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:permutations} + + +\subsubsection{描述} +Given a collection of numbers, return all possible permutations. + +For example, +\fn{[1,2,3]} have the following permutations: +\fn{[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2]}, and \fn{[3,2,1]}. + + +\subsection{next_permutation()} +偷懒的做法,可以直接使用\fn{std::next_permutation()}。如果是在OJ网站上,可以用这个API偷个懒;如果是在面试中,面试官肯定会让你重新实现。 + +\subsubsection{代码} +\begin{Code} +// LeetCode, Permutations +// 时间复杂度O(n!),空间复杂度O(1) +class Solution { +public: + vector > permute(vector &num) { + vector > result; + sort(num.begin(), num.end()); + + do { + result.push_back(num); + } while(next_permutation(num.begin(), num.end())); + return result; + } +}; +\end{Code} + + +\subsection{重新实现next_permutation()} +见第 \S \ref{sec:next-permutation} 节。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Permutations +// 重新实现 next_permutation() +// 时间复杂度O(n!),空间复杂度O(1) +class Solution { +public: + vector> permute(vector& num) { + sort(num.begin(), num.end()); + + vector> permutations; + + do { + permutations.push_back(num); + } while (next_permutation(num.begin(), num.end())); // 见第2.1节 + + return permutations; + } +}; +\end{Code} + + +\subsection{递归} +本题是求路径本身,求所有解,函数参数需要标记当前走到了哪步,还需要中间结果的引用,最终结果的引用。 + +扩展节点,每次从左到右,选一个没有出现过的元素。 + +本题不需要判重,因为状态装换图是一颗有层次的树。收敛条件是当前走到了最后一个元素。 + +\subsubsection{代码} +\begin{Code} +// LeetCode, Permutations +// 深搜,增量构造法 +// 时间复杂度O(n!),空间复杂度O(n) +class Solution { +public: + vector > permute(vector& num) { + sort(num.begin(), num.end()); + + vector> result; + vector path; // 中间结果 + + dfs(num, path, result); + return result; + } +private: + void dfs(const vector& num, vector &path, + vector > &result) { + if (path.size() == num.size()) { // 收敛条件 + result.push_back(path); + return; + } + + // 扩展状态 + for (auto i : num) { + // 查找 i 是否在path 中出现过 + auto pos = find(path.begin(), path.end(), i); + + if (pos == path.end()) { + path.push_back(i); + dfs(num, path, result); + path.pop_back(); + } + } + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Next Permutation, 见 \S \ref{sec:next-permutation} +\item Permutation Sequence, 见 \S \ref{sec:permutation-sequence} +\item Permutations II, 见 \S \ref{sec:permutations-ii} +\item Combinations, 见 \S \ref{sec:combinations} +\myenddot + + +\section{Permutations II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:permutations-ii} + + +\subsubsection{描述} +Given a collection of numbers that might contain duplicates, return all possible unique permutations. + +For example, +\fn{[1,1,2]} have the following unique permutations: +\fn{[1,1,2], [1,2,1]}, and \fn{[2,1,1]}. + + +\subsection{next_permutation()} +直接使用\fn{std::next_permutation()},代码与上一题相同。 + + +\subsection{重新实现next_permutation()} +重新实现\fn{std::next_permutation()},代码与上一题相同。 + + +\subsection{递归} +递归函数\fn{permute()}的参数\fn{p},是中间结果,它的长度又能标记当前走到了哪一步,用于判断收敛条件。 + +扩展节点,每次从小到大,选一个没有被用光的元素,直到所有元素被用光。 + +本题不需要判重,因为状态装换图是一颗有层次的树。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Permutations II +// 深搜,时间复杂度O(n!),空间复杂度O(n) +class Solution { +public: + vector > permuteUnique(vector& num) { + sort(num.begin(), num.end()); + + unordered_map count_map; // 记录每个元素的出现次数 + for_each(num.begin(), num.end(), [&count_map](int e) { + if (count_map.find(e) != count_map.end()) + count_map[e]++; + else + count_map[e] = 1; + }); + + // 将map里的pair拷贝到一个vector里 + vector > elems; + for_each(count_map.begin(), count_map.end(), + [&elems](const pair &e) { + elems.push_back(e); + }); + + vector> result; // 最终结果 + vector p; // 中间结果 + + n = num.size(); + permute(elems.begin(), elems.end(), p, result); + return result; + } + +private: + size_t n; + typedef vector >::const_iterator Iter; + + void permute(Iter first, Iter last, vector &p, + vector > &result) { + if (n == p.size()) { // 收敛条件 + result.push_back(p); + } + + // 扩展状态 + for (auto i = first; i != last; i++) { + int count = 0; // 统计 *i 在p中出现过多少次 + for (auto j = p.begin(); j != p.end(); j++) { + if (i->first == *j) { + count ++; + } + } + if (count < i->second) { + p.push_back(i->first); + permute(first, last, p, result); + p.pop_back(); // 撤销动作,返回上一层 + } + } + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Next Permutation, 见 \S \ref{sec:next-permutation} +\item Permutation Sequence, 见 \S \ref{sec:permutation-sequence} +\item Permutations, 见 \S \ref{sec:permutations} +\item Combinations, 见 \S \ref{sec:combinations} +\myenddot + + +\section{Combinations} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:combinations} + + +\subsubsection{描述} +Given two integers $n$ and $k$, return all possible combinations of $k$ numbers out of $1 ... n$. + +For example, +If $n = 4$ and $k = 2$, a solution is: +\begin{Code} +[ + [2,4], + [3,4], + [2,3], + [1,2], + [1,3], + [1,4], +] +\end{Code} + + +\subsection{递归} +\begin{Code} +// LeetCode, Combinations +// 深搜,递归 +// 时间复杂度O(n!),空间复杂度O(n) +class Solution { +public: + vector > combine(int n, int k) { + vector > result; + vector path; + dfs(n, k, 1, 0, path, result); + return result; + } +private: + // start,开始的数, cur,已经选择的数目 + static void dfs(int n, int k, int start, int cur, + vector &path, vector > &result) { + if (cur == k) { + result.push_back(path); + } + for (int i = start; i <= n; ++i) { + path.push_back(i); + dfs(n, k, i + 1, cur + 1, path, result); + path.pop_back(); + } + } +}; +\end{Code} + + +\subsection{迭代} +\begin{Code} +// LeetCode, Combinations +// use prev_permutation() +// 时间复杂度O((n-k)!),空间复杂度O(n) +class Solution { +public: + vector > combine(int n, int k) { + vector values(n); + iota(values.begin(), values.end(), 1); + + vector select(n, false); + fill_n(select.begin(), k, true); + + vector > result; + do{ + vector one(k); + for (int i = 0, index = 0; i < n; ++i) + if (select[i]) + one[index++] = values[i]; + result.push_back(one); + } while(prev_permutation(select.begin(), select.end())); + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Next Permutation, 见 \S \ref{sec:next-permutation} +\item Permutation Sequence, 见 \S \ref{sec:permutation-sequence} +\item Permutations, 见 \S \ref{sec:permutations} +\item Permutations II, 见 \S \ref{sec:permutations-ii} +\myenddot + + +\section{Letter Combinations of a Phone Number } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:letter-combinations-of-a-phone-number } + + +\subsubsection{描述} +Given a digit string, return all possible letter combinations that the number could represent. + +A mapping of digit to letters (just like on the telephone buttons) is given below. + +\begin{center} +\includegraphics[width=150pt]{phone-keyboard.png}\\ +\figcaption{Phone Keyboard}\label{fig:phone-keyboard} +\end{center} + +\textbf{Input:}Digit string \code{"23"} + +\textbf{Output:} \code{["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]}. + +\textbf{Note:} +Although the above answer is in lexicographical order, your answer could be in any order you want. + + +\subsubsection{分析} +无 + + +\subsection{递归} +\begin{Code} +// LeetCode, Letter Combinations of a Phone Number +// 时间复杂度O(3^n),空间复杂度O(n) +class Solution { +public: + const vector keyboard { " ", "", "abc", "def", // '0','1','2',... + "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" }; + + vector letterCombinations (const string &digits) { + vector result; + dfs(digits, 0, "", result); + return result; + } + + void dfs(const string &digits, size_t cur, string path, + vector &result) { + if (cur == digits.size()) { + result.push_back(path); + return; + } + for (auto c : keyboard[digits[cur] - '0']) { + dfs(digits, cur + 1, path + c, result); + } + } +}; +\end{Code} + + +\subsection{迭代} +\begin{Code} +// LeetCode, Letter Combinations of a Phone Number +// 时间复杂度O(3^n),空间复杂度O(1) +class Solution { +public: + const vector keyboard { " ", "", "abc", "def", // '0','1','2',... + "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" }; + + vector letterCombinations (const string &digits) { + vector result(1, ""); + for (auto d : digits) { + const size_t n = result.size(); + const size_t m = keyboard[d - '0'].size(); + + result.resize(n * m); + for (size_t i = 0; i < m; ++i) + copy(result.begin(), result.begin() + n, result.begin() + n * i); + + for (size_t i = 0; i < m; ++i) { + auto begin = result.begin(); + for_each(begin + n * i, begin + n * (i+1), [&](string &s) { + s += keyboard[d - '0'][i]; + }); + } + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot diff --git a/leetcode/C++/chapDFS.tex b/leetcode/C++/chapDFS.tex new file mode 100644 index 00000000..87c4c7bf --- /dev/null +++ b/leetcode/C++/chapDFS.tex @@ -0,0 +1,1123 @@ +\chapter{深度优先搜索} + + +\section{Palindrome Partitioning} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:palindrome-partitioning} + + +\subsubsection{描述} +Given a string s, partition s such that every substring of the partition is a palindrome. + +Return all possible palindrome partitioning of s. + +For example, given \code{s = "aab"}, +Return +\begin{Code} + [ + ["aa","b"], + ["a","a","b"] + ] +\end{Code} + + +\subsubsection{分析} +在每一步都可以判断中间结果是否为合法结果,用回溯法。 + +一个长度为n的字符串,有$n-1$个地方可以砍断,每个地方可断可不断,因此复杂度为$O(2^{n-1})$ + + +\subsubsection{深搜1} +\begin{Code} +//LeetCode, Palindrome Partitioning +// 时间复杂度O(2^n),空间复杂度O(n) +class Solution { +public: + vector> partition(string s) { + vector> result; + vector path; // 一个partition方案 + dfs(s, path, result, 0, 1); + return result; + } + + // s[0, prev-1]之间已经处理,保证是回文串 + // prev 表示s[prev-1]与s[prev]之间的空隙位置,start同理 + void dfs(string &s, vector& path, + vector> &result, size_t prev, size_t start) { + if (start == s.size()) { // 最后一个隔板 + if (isPalindrome(s, prev, start - 1)) { // 必须使用 + path.push_back(s.substr(prev, start - prev)); + result.push_back(path); + path.pop_back(); + } + return; + } + // 不断开 + dfs(s, path, result, prev, start + 1); + // 如果[prev, start-1] 是回文,则可以断开,也可以不断开(上一行已经做了) + if (isPalindrome(s, prev, start - 1)) { + // 断开 + path.push_back(s.substr(prev, start - prev)); + dfs(s, path, result, start, start + 1); + path.pop_back(); + } + } + + bool isPalindrome(const string &s, int start, int end) { + while (start < end && s[start] == s[end]) { + ++start; + --end; + } + return start >= end; + } +}; +\end{Code} + +\subsubsection{深搜2} +另一种写法,更加简洁。这种写法也在 Combination Sum, Combination Sum II 中出现过。 +\begin{Code} +//LeetCode, Palindrome Partitioning +// 时间复杂度O(2^n),空间复杂度O(n) +class Solution { +public: + vector> partition(string s) { + vector> result; + vector path; // 一个partition方案 + DFS(s, path, result, 0); + return result; + } + // 搜索必须以s[start]开头的partition方案 + void DFS(string &s, vector& path, + vector> &result, int start) { + if (start == s.size()) { + result.push_back(path); + return; + } + for (int i = start; i < s.size(); i++) { + if (isPalindrome(s, start, i)) { // 从i位置砍一刀 + path.push_back(s.substr(start, i - start + 1)); + DFS(s, path, result, i + 1); // 继续往下砍 + path.pop_back(); // 撤销上上行 + } + } + } + bool isPalindrome(const string &s, int start, int end) { + while (start < end && s[start] == s[end]) { + ++start; + --end; + } + return start >= end; + } +}; +\end{Code} + + +\subsubsection{动规} +\begin{Code} +// LeetCode, Palindrome Partitioning +// 动规,时间复杂度O(n^2),空间复杂度O(1) +class Solution { +public: + vector > partition(string s) { + const int n = s.size(); + bool p[n][n]; // whether s[i,j] is palindrome + fill_n(&p[0][0], n * n, false); + for (int i = n - 1; i >= 0; --i) + for (int j = i; j < n; ++j) + p[i][j] = s[i] == s[j] && ((j - i < 2) || p[i + 1][j - 1]); + + vector > sub_palins[n]; // sub palindromes of s[0,i] + for (int i = n - 1; i >= 0; --i) { + for (int j = i; j < n; ++j) + if (p[i][j]) { + const string palindrome = s.substr(i, j - i + 1); + if (j + 1 < n) { + for (auto v : sub_palins[j + 1]) { + v.insert(v.begin(), palindrome); + sub_palins[i].push_back(v); + } + } else { + sub_palins[i].push_back(vector { palindrome }); + } + } + } + return sub_palins[0]; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item Palindrome Partitioning II,见 \S \ref{sec:palindrome-partitioning-ii} +\myenddot + + +\section{Unique Paths} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:unique-paths} + + +\subsubsection{描述} +A robot is located at the top-left corner of a $m \times n$ grid (marked 'Start' in the diagram below). + +The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below). + +How many possible unique paths are there? + +\begin{center} +\includegraphics[width=200pt]{robot-maze.png}\\ +\figcaption{Above is a $3 \times 7$ grid. How many possible unique paths are there?}\label{fig:unique-paths} +\end{center} + +\textbf{Note}: $m$ and $n$ will be at most 100. + + +\subsection{深搜} +深搜,小集合可以过,大集合会超时 + +\subsubsection{代码} +\begin{Code} +// LeetCode, Unique Paths +// 深搜,小集合可以过,大集合会超时 +// 时间复杂度O(n^4),空间复杂度O(n) +class Solution { +public: + int uniquePaths(int m, int n) { + if (m < 1 || n < 1) return 0; // 终止条件 + + if (m == 1 && n == 1) return 1; // 收敛条件 + + return uniquePaths(m - 1, n) + uniquePaths(m, n - 1); + } +}; +\end{Code} + + +\subsection{备忘录法} +给前面的深搜,加个缓存,就可以过大集合了。即备忘录法。 + +\subsubsection{代码} +\begin{Code} +// LeetCode, Unique Paths +// 深搜 + 缓存,即备忘录法 +// 时间复杂度O(n^2),空间复杂度O(n^2) +class Solution { +public: + int uniquePaths(int m, int n) { + // 0行和0列未使用 + this->f = vector >(m + 1, vector(n + 1, 0)); + return dfs(m, n); + } +private: + vector > f; // 缓存 + + int dfs(int x, int y) { + if (x < 1 || y < 1) return 0; // 数据非法,终止条件 + + if (x == 1 && y == 1) return 1; // 回到起点,收敛条件 + + return getOrUpdate(x - 1, y) + getOrUpdate(x, y - 1); + } + + int getOrUpdate(int x, int y) { + if (f[x][y] > 0) return f[x][y]; + else return f[x][y] = dfs(x, y); + } +}; +\end{Code} + + +\subsection{动规} +既然可以用备忘录法自顶向下解决,也一定可以用动规自底向上解决。 + +设状态为\fn{f[i][j]},表示从起点$(1,1)$到达$(i,j)$的路线条数,则状态转移方程为: +\begin{Code} +f[i][j]=f[i-1][j]+f[i][j-1] +\end{Code} + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Unique Paths +// 动规,滚动数组 +// 时间复杂度O(n^2),空间复杂度O(n) +class Solution { +public: + int uniquePaths(int m, int n) { + vector f(n, 0); + f[0] = 1; + for (int i = 0; i < m; i++) { + for (int j = 1; j < n; j++) { + // 左边的f[j],表示更新后的f[j],与公式中的f[i[[j]对应 + // 右边的f[j],表示老的f[j],与公式中的f[i-1][j]对应 + f[j] = f[j - 1] + f[j]; + } + } + return f[n - 1]; + } +}; +\end{Code} + + +\subsection{数学公式} +一个$m$行,$n$列的矩阵,机器人从左上走到右下总共需要的步数是$m+n-2$,其中向下走的步数是$m-1$,因此问题变成了在$m+n-2$个操作中,选择$m–1$个时间点向下走,选择方式有多少种。即 $C_{m+n-2}^{m-1}$ 。 + +\subsubsection{代码} +\begin{Code} +// LeetCode, Unique Paths +// 数学公式 +class Solution { +public: + typedef long long int64_t; + // 求阶乘, n!/(start-1)!,即 n*(n-1)...start,要求 n >= 1 + static int64_t factor(int n, int start = 1) { + int64_t ret = 1; + for(int i = start; i <= n; ++i) + ret *= i; + return ret; + } + // 求组合数 C_n^k + static int64_t combination(int n, int k) { + // 常数优化 + if (k == 0) return 1; + if (k == 1) return n; + + int64_t ret = factor(n, k+1); + ret /= factor(n - k); + return ret; + } + + int uniquePaths(int m, int n) { + // max 可以防止n和k差距过大,从而防止combination()溢出 + return combination(m+n-2, max(m-1, n-1)); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Unique Paths II,见 \S \ref{sec:unique-paths-ii} +\item Minimum Path Sum, 见 \S \ref{sec:minimum-path-sum} +\myenddot + + +\section{Unique Paths II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:unique-paths-ii} + + +\subsubsection{描述} +Follow up for "Unique Paths": + +Now consider if some obstacles are added to the grids. How many unique paths would there be? + +An obstacle and empty space is marked as 1 and 0 respectively in the grid. + +For example, + +There is one obstacle in the middle of a $3 \times 3$ grid as illustrated below. +\begin{Code} +[ + [0,0,0], + [0,1,0], + [0,0,0] +] +\end{Code} + +The total number of unique paths is 2. + +Note: $m$ and $n$ will be at most 100. + + +\subsection{备忘录法} +在上一题的基础上改一下即可。相比动规,简单得多。 + +\subsubsection{代码} +\begin{Code} +// LeetCode, Unique Paths II +// 深搜 + 缓存,即备忘录法 +class Solution { +public: + int uniquePathsWithObstacles(vector > &obstacleGrid) { + const int m = obstacleGrid.size(); + const int n = obstacleGrid[0].size(); + // 0行和0列未使用 + this->f = vector >(m + 1, vector(n + 1, 0)); + return dfs(obstacleGrid, m, n); + } +private: + vector > f; // 缓存 + + int dfs(const vector > &obstacleGrid, + int x, int y) { + if (x < 1 || y < 1) return 0; // 数据非法,终止条件 + + // (x,y)是障碍 + if (obstacleGrid[x-1][y-1]) return 0; + + if (x == 1 and y == 1) return 1; // 回到起点,收敛条件 + + return getOrUpdate(obstacleGrid, x - 1, y) + + getOrUpdate(obstacleGrid, x, y - 1); + } + + int getOrUpdate(const vector > &obstacleGrid, + int x, int y) { + if (f[x][y] > 0) return f[x][y]; + else return f[x][y] = dfs(obstacleGrid, x, y); + } +}; +\end{Code} + + +\subsection{动规} +与上一题类似,但要特别注意第一列的障碍。在上一题中,第一列全部是1,但是在这一题中不同,第一列如果某一行有障碍物,那么后面的行应该为0。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Unique Paths II +// 动规,滚动数组 +// 时间复杂度O(n^2),空间复杂度O(n) +class Solution { +public: + int uniquePathsWithObstacles(vector > &obstacleGrid) { + const int m = obstacleGrid.size(); + const int n = obstacleGrid[0].size(); + if (obstacleGrid[0][0] || obstacleGrid[m-1][n-1]) return 0; + + vector f(n, 0); + f[0] = obstacleGrid[0][0] ? 0 : 1; + + for (int i = 0; i < m; i++) + for (int j = 0; j < n; j++) + f[j] = obstacleGrid[i][j] ? 0 : (j == 0 ? 0 : f[j - 1]) + f[j]; + + return f[n - 1]; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Unique Paths,见 \S \ref{sec:unique-paths} +\item Minimum Path Sum, 见 \S \ref{sec:minimum-path-sum} +\myenddot + + +\section{N-Queens} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:n-queens} + + +\subsubsection{描述} +The \emph{n-queens puzzle} is the problem of placing n queens on an $n \times n$ chessboard such that no two queens attack each other. + +\begin{center} +\includegraphics{8-queens.png}\\ +\figcaption{Eight Queens}\label{fig:8-queens} +\end{center} + +Given an integer $n$, return all distinct solutions to the n-queens puzzle. + +Each solution contains a distinct board configuration of the n-queens' placement, where \fn{'Q'} and \fn{'.'} both indicate a queen and an empty space respectively. + +For example, +There exist two distinct solutions to the 4-queens puzzle: +\begin{Code} +[ + [".Q..", // Solution 1 + "...Q", + "Q...", + "..Q."], + + ["..Q.", // Solution 2 + "Q...", + "...Q", + ".Q.."] +] +\end{Code} + + +\subsubsection{分析} +经典的深搜题。 + +\subsubsection{代码} +\begin{Code} +// LeetCode, N-Queens +// 深搜+剪枝 +// 时间复杂度O(n!),空间复杂度O(n) +class Solution { +public: + vector > solveNQueens(int n) { + this->columns = vector(n, 0); + this->main_diag = vector(2 * n, 0); + this->anti_diag = vector(2 * n, 0); + + vector > result; + vector C(n, 0); // C[i]表示第i行皇后所在的列编号 + dfs(C, result, 0); + return result; + } +private: + // 这三个变量用于剪枝 + vector columns; // 表示已经放置的皇后占据了哪些列 + vector main_diag; // 占据了哪些主对角线 + vector anti_diag; // 占据了哪些副对角线 + + void dfs(vector &C, vector > &result, int row) { + const int N = C.size(); + if (row == N) { // 终止条件,也是收敛条件,意味着找到了一个可行解 + vector solution; + for (int i = 0; i < N; ++i) { + string s(N, '.'); + for (int j = 0; j < N; ++j) { + if (j == C[i]) s[j] = 'Q'; + } + solution.push_back(s); + } + result.push_back(solution); + return; + } + + for (int j = 0; j < N; ++j) { // 扩展状态,一列一列的试 + const bool ok = columns[j] == 0 && main_diag[row + j] == 0 && + anti_diag[row - j + N] == 0; + if (!ok) continue; // 剪枝:如果合法,继续递归 + // 执行扩展动作 + C[row] = j; + columns[j] = main_diag[row + j] = anti_diag[row - j + N] = 1; + dfs(C, result, row + 1); + // 撤销动作 + // C[row] = 0; + columns[j] = main_diag[row + j] = anti_diag[row - j + N] = 0; + } + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item N-Queens II,见 \S \ref{sec:n-queens-ii} +\myenddot + + +\section{N-Queens II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:n-queens-ii} + + +\subsubsection{描述} +Follow up for N-Queens problem. + +Now, instead outputting board configurations, return the total number of distinct solutions. + + +\subsubsection{分析} +只需要输出解的个数,不需要输出所有解,代码要比上一题简化很多。设一个全局计数器,每找到一个解就增1。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, N-Queens II +// 深搜+剪枝 +// 时间复杂度O(n!),空间复杂度O(n) +class Solution { +public: + int totalNQueens(int n) { + this->count = 0; + this->columns = vector(n, 0); + this->main_diag = vector(2 * n, 0); + this->anti_diag = vector(2 * n, 0); + + vector C(n, 0); // C[i]表示第i行皇后所在的列编号 + dfs(C, 0); + return this->count; + } +private: + int count; // 解的个数 + // 这三个变量用于剪枝 + vector columns; // 表示已经放置的皇后占据了哪些列 + vector main_diag; // 占据了哪些主对角线 + vector anti_diag; // 占据了哪些副对角线 + + void dfs(vector &C, int row) { + const int N = C.size(); + if (row == N) { // 终止条件,也是收敛条件,意味着找到了一个可行解 + ++this->count; + return; + } + + for (int j = 0; j < N; ++j) { // 扩展状态,一列一列的试 + const bool ok = columns[j] == 0 && + main_diag[row + j] == 0 && + anti_diag[row - j + N] == 0; + if (!ok) continue; // 剪枝:如果合法,继续递归 + // 执行扩展动作 + C[row] = j; + columns[j] = main_diag[row + j] = + anti_diag[row - j + N] = 1; + dfs(C, row + 1); + // 撤销动作 + // C[row] = 0; + columns[j] = main_diag[row + j] = + anti_diag[row - j + N] = 0; + } + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item N-Queens,见 \S \ref{sec:n-queens} +\myenddot + + +\section{Restore IP Addresses} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:restore-ip-addresses} + + +\subsubsection{描述} +Given a string containing only digits, restore it by returning all possible valid IP address combinations. + +For example: +Given \code{"25525511135"}, + +return \code{["255.255.11.135", "255.255.111.35"]}. (Order does not matter) + + +\subsubsection{分析} +必须要走到底部才能判断解是否合法,深搜。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Restore IP Addresses +// 时间复杂度O(n^4),空间复杂度O(n) +class Solution { +public: + vector restoreIpAddresses(string s) { + vector result; + string ip; // 存放中间结果 + dfs(s, 0, 0, ip, result); + return result; + } + + /** + * @brief 解析字符串 + * @param[in] s 字符串,输入数据 + * @param[in] startIndex 从s的哪里开始 + * @param[in] step 当前步骤编号,从0开始编号,取值为0,1,2,3,4表示结束了 + * @param[out] intermediate 当前解析出来的中间结果 + * @param[out] result 存放所有可能的IP地址 + * @return 无 + */ + void dfs(string s, size_t start, size_t step, string ip, + vector &result) { + if (start == s.size() && step == 4) { // 找到一个合法解 + ip.resize(ip.size() - 1); + result.push_back(ip); + return; + } + + if (s.size() - start > (4 - step) * 3) + return; // 剪枝 + if (s.size() - start < (4 - step)) + return; // 剪枝 + + int num = 0; + for (size_t i = start; i < start + 3; i++) { + num = num * 10 + (s[i] - '0'); + + if (num <= 255) { // 当前结点合法,则继续往下递归 + ip += s[i]; + dfs(s, i + 1, step + 1, ip + '.', result); + } + if (num == 0) break; // 不允许前缀0,但允许单个0 + } + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{Combination Sum} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:combination-sum} + + +\subsubsection{描述} +Given a set of candidate numbers ($C$) and a target number ($T$), find all unique combinations in $C$ where the candidate numbers sums to $T$. + +The same repeated number may be chosen from $C$ \emph{unlimited} number of times. + +Note: +\begindot +\item All numbers (including target) will be positive integers. +\item Elements in a combination ($a_1, a_2, ..., a_k$) must be in non-descending order. (ie, $a_1 \leq a_2 \leq ... \leq a_k$). +\item The solution set must not contain duplicate combinations. +\myenddot + +For example, given candidate set \fn{2,3,6,7} and target \fn{7}, +A solution set is: +\begin{Code} +[7] +[2, 2, 3] +\end{Code} + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Combination Sum +// 时间复杂度O(n!),空间复杂度O(n) +class Solution { +public: + vector > combinationSum(vector &nums, int target) { + sort(nums.begin(), nums.end()); + vector > result; // 最终结果 + vector intermediate; // 中间结果 + dfs(nums, target, 0, intermediate, result); + return result; + } + +private: + void dfs(vector& nums, int gap, int start, vector& intermediate, + vector > &result) { + if (gap == 0) { // 找到一个合法解 + result.push_back(intermediate); + return; + } + for (size_t i = start; i < nums.size(); i++) { // 扩展状态 + if (gap < nums[i]) return; // 剪枝 + + intermediate.push_back(nums[i]); // 执行扩展动作 + dfs(nums, gap - nums[i], i, intermediate, result); + intermediate.pop_back(); // 撤销动作 + } + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Combination Sum II ,见 \S \ref{sec:combination-sum-ii} +\myenddot + + +\section{Combination Sum II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:combination-sum-ii} + + +\subsubsection{描述} +Given a set of candidate numbers ($C$) and a target number ($T$), find all unique combinations in $C$ where the candidate numbers sums to $T$. + +The same repeated number may be chosen from $C$ \emph{once} number of times. + +Note: +\begindot +\item All numbers (including target) will be positive integers. +\item Elements in a combination ($a_1, a_2, ..., a_k$) must be in non-descending order. (ie, $a_1 > a_2 > ... > a_k$). +\item The solution set must not contain duplicate combinations. +\myenddot + +For example, given candidate set \fn{10,1,2,7,6,1,5} and target \fn{8}, +A solution set is: +\begin{Code} +[1, 7] +[1, 2, 5] +[2, 6] +[1, 1, 6] +\end{Code} + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Combination Sum II +// 时间复杂度O(n!),空间复杂度O(n) +class Solution { +public: + vector > combinationSum2(vector &nums, int target) { + sort(nums.begin(), nums.end()); // 跟第 50 行配合, + // 确保每个元素最多只用一次 + vector > result; + vector intermediate; + dfs(nums, target, 0, intermediate, result); + return result; + } +private: + // 使用nums[start, nums.size())之间的元素,能找到的所有可行解 + static void dfs(vector &nums, int gap, int start, + vector &intermediate, vector > &result) { + if (gap == 0) { // 找到一个合法解 + result.push_back(intermediate); + return; + } + + int previous = -1; + for (size_t i = start; i < nums.size(); i++) { + // 如果上一轮循环没有选nums[i],则本次循环就不能再选nums[i], + // 确保nums[i]最多只用一次 + if (previous == nums[i]) continue; + + if (gap < nums[i]) return; // 剪枝 + + previous = nums[i]; + + intermediate.push_back(nums[i]); + dfs(nums, gap - nums[i], i + 1, intermediate, result); + intermediate.pop_back(); // 恢复环境 + } + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Combination Sum ,见 \S \ref{sec:combination-sum} +\myenddot + + +\section{Generate Parentheses } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:generate-parentheses} + + +\subsubsection{描述} +Given $n$ pairs of parentheses, write a function to generate all combinations of well-formed parentheses. + +For example, given $n = 3$, a solution set is: +\begin{Code} +"((()))", "(()())", "(())()", "()(())", "()()()" +\end{Code} + +\subsubsection{分析} +小括号串是一个递归结构,跟单链表、二叉树等递归结构一样,首先想到用递归。 + +一步步构造字符串。当左括号出现次数$ generateParenthesis(int n) { + vector result; + if (n > 0) generate(n, "", 0, 0, result); + return result; + } + // l 表示 ( 出现的次数, r 表示 ) 出现的次数 + void generate(int n, string s, int l, int r, vector &result) { + if (l == n) { + result.push_back(s.append(n - r, ')')); + return; + } + generate(n, s + '(', l + 1, r, result); + if (l > r) generate(n, s + ")", l, r + 1, result); + } +}; +\end{Code} + + +\subsubsection{代码2} +另一种递归写法,更加简洁。 +\begin{Code} +// LeetCode, Generate Parentheses +// @author 连城 (http://weibo.com/lianchengzju) +class Solution { +public: + vector generateParenthesis (int n) { + if (n == 0) return vector(1, ""); + if (n == 1) return vector (1, "()"); + vector result; + + for (int i = 0; i < n; ++i) + for (auto inner : generateParenthesis (i)) + for (auto outer : generateParenthesis (n - 1 - i)) + result.push_back ("(" + inner + ")" + outer); + + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Valid Parentheses, 见 \S \ref{sec:valid-parentheses} +\item Longest Valid Parentheses, 见 \S \ref{sec:longest-valid-parentheses} +\myenddot + + +\section{Sudoku Solver} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:sudoku-solver} + + +\subsubsection{描述} +Write a program to solve a Sudoku puzzle by filling the empty cells. + +Empty cells are indicated by the character \fn{'.'}. + +You may assume that there will be only one unique solution. + +\begin{center} +\includegraphics[width=150pt]{sudoku.png}\\ +\figcaption{A sudoku puzzle...}\label{fig:sudoku} +\end{center} + +\begin{center} +\includegraphics[width=150pt]{sudoku-solution.png}\\ +\figcaption{...and its solution numbers marked in red}\label{fig:sudoku-solution} +\end{center} + + +\subsubsection{分析} +无。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Sudoku Solver +// 时间复杂度O(9^4),空间复杂度O(1) +class Solution { +public: + bool solveSudoku(vector > &board) { + for (int i = 0; i < 9; ++i) + for (int j = 0; j < 9; ++j) { + if (board[i][j] == '.') { + for (int k = 0; k < 9; ++k) { + board[i][j] = '1' + k; + if (isValid(board, i, j) && solveSudoku(board)) + return true; + board[i][j] = '.'; + } + return false; + } + } + return true; + } +private: + // 检查 (x, y) 是否合法 + bool isValid(const vector > &board, int x, int y) { + int i, j; + for (i = 0; i < 9; i++) // 检查 y 列 + if (i != x && board[i][y] == board[x][y]) + return false; + for (j = 0; j < 9; j++) // 检查 x 行 + if (j != y && board[x][j] == board[x][y]) + return false; + for (i = 3 * (x / 3); i < 3 * (x / 3 + 1); i++) + for (j = 3 * (y / 3); j < 3 * (y / 3 + 1); j++) + if ((i != x || j != y) && board[i][j] == board[x][y]) + return false; + return true; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Valid Sudoku, 见 \S \ref{sec:valid-sudoku} +\myenddot + + +\section{Word Search} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:word-search} + + +\subsubsection{描述} +Given a 2D board and a word, find if the word exists in the grid. + +The word can be constructed from letters of sequentially adjacent cell, where \fn{"adjacent"} cells are those horizontally or vertically neighbouring. The same letter cell may not be used more than once. + +For example, +Given board = +\begin{Code} +[ + ["ABCE"], + ["SFCS"], + ["ADEE"] +] +\end{Code} +word = \fn{"ABCCED"}, -> returns \fn{true},\\ +word = \fn{"SEE"}, -> returns \fn{true},\\ +word = \fn{"ABCB"}, -> returns \fn{false}. + + +\subsubsection{分析} +无。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Word Search +// 深搜,递归 +// 时间复杂度O(n^2*m^2),空间复杂度O(n^2) +class Solution { +public: + bool exist(vector > &board, string word) { + const int m = board.size(); + const int n = board[0].size(); + vector > visited(m, vector(n, false)); + for (int i = 0; i < m; ++i) + for (int j = 0; j < n; ++j) + if (dfs(board, word, 0, i, j, visited)) + return true; + return false; + } +private: + static bool dfs(const vector > &board, const string &word, + int index, int x, int y, vector > &visited) { + if (index == word.size()) + return true; // 收敛条件 + + if (x < 0 || y < 0 || x >= board.size() || y >= board[0].size()) + return false; // 越界,终止条件 + + if (visited[x][y]) return false; // 已经访问过,剪枝 + + if (board[x][y] != word[index]) return false; // 不相等,剪枝 + + visited[x][y] = true; + bool ret = dfs(board, word, index + 1, x - 1, y, visited) || // 上 + dfs(board, word, index + 1, x + 1, y, visited) || // 下 + dfs(board, word, index + 1, x, y - 1, visited) || // 左 + dfs(board, word, index + 1, x, y + 1, visited); // 右 + visited[x][y] = false; + return ret; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{小结} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:dfs-template} + + +\subsection{适用场景} + +\textbf{输入数据}:如果是递归数据结构,如单链表,二叉树,集合,则百分之百可以用深搜;如果是非递归数据结构,如一维数组,二维数组,字符串,图,则概率小一些。 + +\textbf{状态转换图}:树或者图。 + +\textbf{求解目标}:必须要走到最深(例如对于树,必须要走到叶子节点)才能得到一个解,这种情况适合用深搜。 + + +\subsection{思考的步骤} +\begin{enumerate} +\item 是求路径条数,还是路径本身(或动作序列)?深搜最常见的三个问题,求可行解的总数,求一个可行解,求所有可行解。 + \begin{enumerate} + \item 如果是路径条数,则不需要存储路径。 + \item 如果是求路径本身,则要用一个数组\fn{path[]}存储路径。跟宽搜不同,宽搜虽然最终求的也是一条路径,但是需要存储扩展过程中的所有路径,在没找到答案之前所有路径都不能放弃;而深搜,在搜索过程中始终只有一条路径,因此用一个数组就足够了。 + \end{enumerate} + +\item 只要求一个解,还是要求所有解?如果只要求一个解,那找到一个就可以返回;如果要求所有解,找到了一个后,还要继续扩展,直到遍历完。广搜一般只要求一个解,因而不需要考虑这个问题(广搜当然也可以求所有解,这时需要扩展到所有叶子节点,相当于在内存中存储整个状态转换图,非常占内存,因此广搜不适合解这类问题)。 + +\item 如何表示状态?即一个状态需要存储哪些些必要的数据,才能够完整提供如何扩展到下一步状态的所有信息。跟广搜不同,深搜的惯用写法,不是把数据记录在状态\fn{struct}里,而是添加函数参数(有时为了节省递归堆栈,用全局变量),\fn{struct}里的字段与函数参数一一对应。 + +\item 如何扩展状态?这一步跟上一步相关。状态里记录的数据不同,扩展方法就不同。对于固定不变的数据结构(一般题目直接给出,作为输入数据),如二叉树,图等,扩展方法很简单,直接往下一层走,对于隐式图,要先在第1步里想清楚状态所带的数据,想清楚了这点,那如何扩展就很简单了。 + +\item 关于判重 + \begin{enumerate} + \item 是否需要判重?如果状态转换图是一棵树,则不需要判重,因为在遍历过程中不可能重复;如果状态转换图是一个DAG,则需要判重。这一点跟BFS不一样,BFS的状态转换图总是DAG,必须要判重。 + \item 怎样判重?跟广搜相同,见第 \S \ref{sec:bfs-template} 节。同时,DAG说明存在重叠子问题,此时可以用缓存加速,见第8步。 + \end{enumerate} + +\item 终止条件是什么?终止条件是指到了不能扩展的末端节点。对于树,是叶子节点,对于图或隐式图,是出度为0的节点。 + +\item {收敛条件是什么?收敛条件是指找到了一个合法解的时刻。如果是正向深搜(父状态处理完了才进行递归,即父状态不依赖子状态,递归语句一定是在最后,尾递归),则是指是否达到目标状态;如果是逆向深搜(处理父状态时需要先知道子状态的结果,此时递归语句不在最后),则是指是否到达初始状态。 + +由于很多时候终止条件和收敛条件是是合二为一的,因此很多人不区分这两种条件。仔细区分这两种条件,还是很有必要的。 + +为了判断是否到了收敛条件,要在函数接口里用一个参数记录当前的位置(或距离目标还有多远)。如果是求一个解,直接返回这个解;如果是求所有解,要在这里收集解,即把第一步中表示路径的数组\fn{path[]}复制到解集合里。} + +\item 如何加速? + \begin{enumerate} + \item 剪枝。深搜一定要好好考虑怎么剪枝,成本小收益大,加几行代码,就能大大加速。这里没有通用方法,只能具体问题具体分析,要充分观察,充分利用各种信息来剪枝,在中间节点提前返回。 + \item 缓存。 + \begin{enumerate} + \item 前提条件:状态转换图是一个DAG。DAG=>存在重叠子问题=>子问题的解会被重复利用,用缓存自然会有加速效果。如果依赖关系是树状的(例如树,单链表等),没必要加缓存,因为子问题只会一层层往下,用一次就再也不会用到,加了缓存也没什么加速效果。 + \item 具体实现:可以用数组或HashMap。维度简单的,用数组;维度复杂的,用HashMap,C++有\fn{map},C++ 11以后有\fn{unordered_map},比\fn{map}快。 + \end{enumerate} + + \end{enumerate} +\end{enumerate} + +拿到一个题目,当感觉它适合用深搜解决时,在心里面把上面8个问题默默回答一遍,代码基本上就能写出来了。对于树,不需要回答第5和第8个问题。如果读者对上面的经验总结看不懂或感觉“不实用”,很正常,因为这些经验总结是我做了很多题目后总结出来的,从思维的发展过程看,“经验总结”要晚于感性认识,所以这时候建议读者先做做前面的题目,积累一定的感性认识后,再回过头来看这一节的总结,一定会有共鸣。 + + +\subsection{代码模板} + +\begin{Codex}[label=dfs_template.cpp] +/** + * dfs模板. + * @param[in] input 输入数据指针 + * @param[out] path 当前路径,也是中间结果 + * @param[out] result 存放最终结果 + * @param[inout] cur or gap 标记当前位置或距离目标的距离 + * @return 路径长度,如果是求路径本身,则不需要返回长度 + */ +void dfs(type &input, type &path, type &result, int cur or gap) { + if (数据非法) return 0; // 终止条件 + if (cur == input.size()) { // 收敛条件 + // if (gap == 0) { + 将path放入result + } + + if (可以剪枝) return; + + for(...) { // 执行所有可能的扩展动作 + 执行动作,修改path + dfs(input, step + 1 or gap--, result); + 恢复path + } +} +\end{Codex} + + +\subsection{深搜与回溯法的区别} +深搜(Depth-first search, DFS)的定义见\myurl{http://en.wikipedia.org/wiki/Depth_first_search},回溯法(backtracking)的定义见\myurl{http://en.wikipedia.org/wiki/Backtracking} + +\textbf{回溯法 = 深搜 + 剪枝}。一般大家用深搜时,或多或少会剪枝,因此深搜与回溯法没有什么不同,可以在它们之间画上一个等号。本书同时使用深搜和回溯法两个术语,但读者可以认为二者等价。 + +深搜一般用递归(recursion)来实现,这样比较简洁。 + +深搜能够在候选答案生成到一半时,就进行判断,抛弃不满足要求的答案,所以深搜比暴力搜索法要快。 + + +\subsection{深搜与递归的区别} +\label{sec:dfs-vs-recursion} + +深搜经常用递归(recursion)来实现,二者常常同时出现,导致很多人误以为他俩是一个东西。 + +深搜,是逻辑意义上的算法,递归,是一种物理意义上的实现,它和迭代(iteration)是对应的。深搜,可以用递归来实现,也可以用栈来实现;而递归,一般总是用来实现深搜。可以说,\textbf{递归一定是深搜,深搜不一定用递归}。 + +递归有两种加速策略,一种是\textbf{剪枝(prunning)},对中间结果进行判断,提前返回;一种是\textbf{缓存},缓存中间结果,防止重复计算,用空间换时间。 + +其实,递归+缓存,就是 memorization。所谓\textbf{memorization}(翻译为备忘录法,见第 \S \ref{sec:dp-vs-memorization}节),就是"top-down with cache"(自顶向下+缓存),它是Donald Michie 在1968年创造的术语,表示一种优化技术,在top-down 形式的程序中,使用缓存来避免重复计算,从而达到加速的目的。 + +\textbf{memorization 不一定用递归},就像深搜不一定用递归一样,可以在迭代(iterative)中使用 memorization 。\textbf{递归也不一定用 memorization},可以用memorization来加速,但不是必须的。只有当递归使用了缓存,它才是 memorization 。 + +既然递归一定是深搜,为什么很多书籍都同时使用这两个术语呢?在递归味道更浓的地方,一般用递归这个术语,在深搜更浓的场景下,用深搜这个术语,读者心里要弄清楚他俩大部分时候是一回事。在单链表、二叉树等递归数据结构上,递归的味道更浓,这时用递归这个术语;在图、隐式图等数据结构上,深搜的味道更浓,这时用深搜这个术语。 diff --git a/leetcode/C++/chapDivideAndConquer.tex b/leetcode/C++/chapDivideAndConquer.tex new file mode 100644 index 00000000..0876f43f --- /dev/null +++ b/leetcode/C++/chapDivideAndConquer.tex @@ -0,0 +1,91 @@ +\chapter{分治法} + + +\section{Pow(x,n)} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:pow} + + +\subsubsection{描述} +Implement pow(x, n). + + +\subsubsection{分析} +二分法,$x^n = x^{n/2} \times x^{n/2} \times x^{n\%2}$ + + +\subsubsection{代码} +\begin{Code} +//LeetCode, Pow(x, n) +// 二分法,$x^n = x^{n/2} * x^{n/2} * x^{n\%2}$ +// 时间复杂度O(logn),空间复杂度O(1) +class Solution { +public: + double pow(double x, int n) { + if (n < 0) return 1.0 / power(x, -n); + else return power(x, n); + } +private: + double power(double x, int n) { + if (n == 0) return 1; + double v = power(x, n / 2); + if (n % 2 == 0) return v * v; + else return v * v * x; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item Sqrt(x),见 \S \ref{sec:sqrt} +\myenddot + + +\section{Sqrt(x)} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:sqrt} + +\subsubsection{描述} +Implement int \fn{sqrt(int x)}. + +Compute and return the square root of \fn{x}. + + +\subsubsection{分析} +二分查找 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Sqrt(x) +// 二分查找 +// 时间复杂度O(logn),空间复杂度O(1) +class Solution { +public: + int sqrt(int x) { + int left = 1, right = x / 2; + int last_mid; // 记录最近一次mid + + if (x < 2) return x; + + while(left <= right) { + const int mid = left + (right - left) / 2; + if(x / mid > mid) { // 不要用 x > mid * mid,会溢出 + left = mid + 1; + last_mid = mid; + } else if(x / mid < mid) { + right = mid - 1; + } else { + return mid; + } + } + return last_mid; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Pow(x),见 \S \ref{sec:pow} +\myenddot diff --git a/leetcode/C++/chapDynamicProgramming.tex b/leetcode/C++/chapDynamicProgramming.tex new file mode 100644 index 00000000..a6ec1d08 --- /dev/null +++ b/leetcode/C++/chapDynamicProgramming.tex @@ -0,0 +1,1221 @@ +\chapter{动态规划} + + +\section{Triangle} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:triangle} + + +\subsubsection{描述} +Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below. + +For example, given the following triangle +\begin{Code} +[ + [2], + [3,4], + [6,5,7], + [4,1,8,3] +] +\end{Code} +The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11). + +Note: Bonus point if you are able to do this using only $O(n)$ extra space, where n is the total number of rows in the triangle. + + +\subsubsection{分析} +设状态为$f(i, j)$,表示从从位置$(i,j)$出发,路径的最小和,则状态转移方程为 +$$ +f(i,j)=\min\left\{f(i,j+1),f(i+1,j+1)\right\}+(i,j) +$$ + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Triangle +// 时间复杂度O(n^2),空间复杂度O(1) +class Solution { +public: + int minimumTotal (vector>& triangle) { + for (int i = triangle.size() - 2; i >= 0; --i) + for (int j = 0; j < i + 1; ++j) + triangle[i][j] += min(triangle[i + 1][j], + triangle[i + 1][j + 1]); + + return triangle [0][0]; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{Maximum Subarray} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:maximum-subarray} + + +\subsubsection{描述} +Find the contiguous subarray within an array (containing at least one number) which has the largest sum. + +For example, given the array \code{[−2,1,−3,4,−1,2,1,−5,4]}, +the contiguous subarray \code{[4,−1,2,1]} has the largest \code{sum = 6}. + + +\subsubsection{分析} +最大连续子序列和,非常经典的题。 + +当我们从头到尾遍历这个数组的时候,对于数组里的一个整数,它有几种选择呢?它只有两种选择: 1、加入之前的SubArray;2. 自己另起一个SubArray。那什么时候会出现这两种情况呢? + +如果之前SubArray的总体和大于0的话,我们认为其对后续结果是有贡献的。这种情况下我们选择加入之前的SubArray + +如果之前SubArray的总体和为0或者小于0的话,我们认为其对后续结果是没有贡献,甚至是有害的(小于0时)。这种情况下我们选择以这个数字开始,另起一个SubArray。 + +设状态为\fn{f[j]},表示以\fn{S[j]}结尾的最大连续子序列和,则状态转移方程如下: +\begin{eqnarray} +f[j] &=& \max\left\{f[j-1]+S[j],S[j]\right\}, \text{ 其中 }1 \leq j \leq n \nonumber \\ +target &=& \max\left\{f[j]\right\}, \text{ 其中 }1 \leq j \leq n \nonumber +\end{eqnarray} + +解释如下: +\begindot +\item 情况一,S[j]不独立,与前面的某些数组成一个连续子序列,则最大连续子序列和为$f[j-1]+S[j]$。 +\item 情况二,S[j]独立划分成为一段,即连续子序列仅包含一个数S[j],则最大连续子序列和为$S[j]$。 +\myenddot + +其他思路: +\begindot +\item 思路2:直接在i到j之间暴力枚举,复杂度是$O(n^3)$ +\item 思路3:处理后枚举,连续子序列的和等于两个前缀和之差,复杂度$O(n^2)$。 +\item 思路4:分治法,把序列分为两段,分别求最大连续子序列和,然后归并,复杂度$O(n\log n)$ +\item 思路5:把思路2$O(n^2)$的代码稍作处理,得到$O(n)$的算法 +\item 思路6:当成M=1的最大M子段和 +\myenddot + + +\subsubsection{动规} +\begin{Code} +// LeetCode, Maximum Subarray +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int maxSubArray(int A[], int n) { + int result = INT_MIN, f = 0; + for (int i = 0; i < n; ++i) { + f = max(f + A[i], A[i]); + result = max(result, f); + } + return result; + } +}; +\end{Code} + + +\subsubsection{思路5} +\begin{Code} +// LeetCode, Maximum Subarray +// 时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + int maxSubArray(int A[], int n) { + return mcss(A, n); + } +private: + // 思路5,求最大连续子序列和 + static int mcss(int A[], int n) { + int i, result, cur_min; + int *sum = new int[n + 1]; // 前n项和 + + sum[0] = 0; + result = INT_MIN; + cur_min = sum[0]; + for (i = 1; i <= n; i++) { + sum[i] = sum[i - 1] + A[i - 1]; + } + for (i = 1; i <= n; i++) { + result = max(result, sum[i] - cur_min); + cur_min = min(cur_min, sum[i]); + } + delete[] sum; + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Binary Tree Maximum Path Sum,见 \S \ref{sec:binary-tree-maximum-path-sum} +\myenddot + + +\section{Palindrome Partitioning II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:palindrome-partitioning-ii} + + +\subsubsection{描述} +Given a string s, partition s such that every substring of the partition is a palindrome. + +Return the minimum cuts needed for a palindrome partitioning of s. + +For example, given \code{s = "aab"}, + +Return 1 since the palindrome partitioning \code{["aa","b"]} could be produced using 1 cut. + + +\subsubsection{分析} +定义状态\fn{f(i,j)}表示区间\fn{[i,j]}之间最小的cut数,则状态转移方程为 +$$ +f(i,j)=\min\left\{f(i,k)+f(k+1,j)\right\}, i \leq k \leq j, 0 \leq i \leq j= 0; i--) { + for (int j = i; j < n; j++) { + if (s[i] == s[j] && (j - i < 2 || p[i + 1][j - 1])) { + p[i][j] = true; + f[i] = min(f[i], f[j + 1] + 1); + } + } + } + return f[0]; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Palindrome Partitioning,见 \S \ref{sec:palindrome-partitioning} +\myenddot + + +\section{Maximal Rectangle} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:maximal-rectangle} + + +\subsubsection{描述} +Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and return its area. + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Maximal Rectangle +// 时间复杂度O(n^2),空间复杂度O(n) +class Solution { +public: + int maximalRectangle(vector > &matrix) { + if (matrix.empty()) return 0; + + const int m = matrix.size(); + const int n = matrix[0].size(); + vector H(n, 0); + vector L(n, 0); + vector R(n, n); + + int ret = 0; + for (int i = 0; i < m; ++i) { + int left = 0, right = n; + // calculate L(i, j) from left to right + for (int j = 0; j < n; ++j) { + if (matrix[i][j] == '1') { + ++H[j]; + L[j] = max(L[j], left); + } else { + left = j+1; + H[j] = 0; L[j] = 0; R[j] = n; + } + } + // calculate R(i, j) from right to left + for (int j = n-1; j >= 0; --j) { + if (matrix[i][j] == '1') { + R[j] = min(R[j], right); + ret = max(ret, H[j]*(R[j]-L[j])); + } else { + right = j; + } + } + } + return ret; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item 无 +\myenddot + + +\section{Best Time to Buy and Sell Stock III} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:best-time-to-buy-and-sell-stock-iii} + + +\subsubsection{描述} +Say you have an array for which the i-th element is the price of a given stock on day i. + +Design an algorithm to find the maximum profit. You may complete at most two transactions. + +Note: You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). + + +\subsubsection{分析} +设状态$f(i)$,表示区间$[0,i](0 \leq i \leq n-1)$的最大利润,状态$g(i)$,表示区间$[i, n-1](0 \leq i \leq n-1)$的最大利润,则最终答案为$\max\left\{f(i)+g(i)\right\},0 \leq i \leq n-1$。 + +允许在一天内买进又卖出,相当于不交易,因为题目的规定是最多两次,而不是一定要两次。 + +将原数组变成差分数组,本题也可以看做是最大$m$子段和,$m=2$,参考代码:\myurl{https://gist.github.com/soulmachine/5906637} + +\subsubsection{代码} +\begin{Code} +// LeetCode, Best Time to Buy and Sell Stock III +// 时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + int maxProfit(vector& prices) { + if (prices.size() < 2) return 0; + + const int n = prices.size(); + vector f(n, 0); + vector g(n, 0); + + for (int i = 1, valley = prices[0]; i < n; ++i) { + valley = min(valley, prices[i]); + f[i] = max(f[i - 1], prices[i] - valley); + } + + for (int i = n - 2, peak = prices[n - 1]; i >= 0; --i) { + peak = max(peak, prices[i]); + g[i] = max(g[i], peak - prices[i]); + } + + int max_profit = 0; + for (int i = 0; i < n; ++i) + max_profit = max(max_profit, f[i] + g[i]); + + return max_profit; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Best Time to Buy and Sell Stock,见 \S \ref{sec:best-time-to-buy-and-sell-stock} +\item Best Time to Buy and Sell Stock II,见 \S \ref{sec:best-time-to-buy-and-sell-stock-ii} +\myenddot + + +\section{Interleaving String} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:interleaving-string} + + +\subsubsection{描述} +Given $s1, s2, s3$, find whether $s3$ is formed by the interleaving of $s1$ and $s2$. + +For example, Given: \code{s1 = "aabcc", s2 = "dbbca"}, + +When \code{s3 = "aadbbcbcac"}, return true. + +When \code{s3 = "aadbbbaccc"}, return false. + + +\subsubsection{分析} +设状态\fn{f[i][j]},表示\fn{s1[0,i]}和\fn{s2[0,j]},匹配\fn{s3[0, i+j]}。如果s1的最后一个字符等于s3的最后一个字符,则\fn{f[i][j]=f[i-1][j]};如果s2的最后一个字符等于s3的最后一个字符,则\fn{f[i][j]=f[i][j-1]}。因此状态转移方程如下: +\begin{Code} +f[i][j] = (s1[i - 1] == s3 [i + j - 1] && f[i - 1][j]) + || (s2[j - 1] == s3 [i + j - 1] && f[i][j - 1]); +\end{Code} + + +\subsubsection{递归} +\begin{Code} +// LeetCode, Interleaving String +// 递归,会超时,仅用来帮助理解 +class Solution { +public: + bool isInterleave(string s1, string s2, string s3) { + if (s3.length() != s1.length() + s2.length()) + return false; + + return isInterleave(begin(s1), end(s1), begin(s2), end(s2), + begin(s3), end(s3)); + } + + template + bool isInterleave(InIt first1, InIt last1, InIt first2, InIt last2, + InIt first3, InIt last3) { + if (first3 == last3) + return first1 == last1 && first2 == last2; + + return (*first1 == *first3 + && isInterleave(next(first1), last1, first2, last2, + next(first3), last3)) + || (*first2 == *first3 + && isInterleave(first1, last1, next(first2), last2, + next(first3), last3)); + } +}; +\end{Code} + + +\subsubsection{动规} +\begin{Code} +// LeetCode, Interleaving String +// 二维动规,时间复杂度O(n^2),空间复杂度O(n^2) +class Solution { +public: + bool isInterleave(string s1, string s2, string s3) { + if (s3.length() != s1.length() + s2.length()) + return false; + + vector> f(s1.length() + 1, + vector(s2.length() + 1, true)); + + for (size_t i = 1; i <= s1.length(); ++i) + f[i][0] = f[i - 1][0] && s1[i - 1] == s3[i - 1]; + + for (size_t i = 1; i <= s2.length(); ++i) + f[0][i] = f[0][i - 1] && s2[i - 1] == s3[i - 1]; + + for (size_t i = 1; i <= s1.length(); ++i) + for (size_t j = 1; j <= s2.length(); ++j) + f[i][j] = (s1[i - 1] == s3[i + j - 1] && f[i - 1][j]) + || (s2[j - 1] == s3[i + j - 1] && f[i][j - 1]); + + return f[s1.length()][s2.length()]; + } +}; +\end{Code} + + +\subsubsection{动规+滚动数组} +\begin{Code} +// LeetCode, Interleaving String +// 二维动规+滚动数组,时间复杂度O(n^2),空间复杂度O(n) +class Solution { +public: + bool isInterleave(string s1, string s2, string s3) { + if (s1.length() + s2.length() != s3.length()) + return false; + + if (s1.length() < s2.length()) + return isInterleave(s2, s1, s3); + + vector f(s2.length() + 1, true); + + for (size_t i = 1; i <= s2.length(); ++i) + f[i] = s2[i - 1] == s3[i - 1] && f[i - 1]; + + for (size_t i = 1; i <= s1.length(); ++i) { + f[0] = s1[i - 1] == s3[i - 1] && f[0]; + + for (size_t j = 1; j <= s2.length(); ++j) + f[j] = (s1[i - 1] == s3[i + j - 1] && f[j]) + || (s2[j - 1] == s3[i + j - 1] && f[j - 1]); + } + + return f[s2.length()]; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{Scramble String} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:scramble-string} + + +\subsubsection{描述} +Given a string $s1$, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively. + +Below is one possible representation of \code{s1 = "great"}: +\begin{Code} + great + / \ + gr eat + / \ / \ +g r e at + / \ + a t +\end{Code} + +To scramble the string, we may choose any non-leaf node and swap its two children. + +For example, if we choose the node \code{"gr"} and swap its two children, it produces a scrambled string \code{"rgeat"}. +\begin{Code} + rgeat + / \ + rg eat + / \ / \ +r g e at + / \ + a t +\end{Code} + +We say that \code{"rgeat"} is a scrambled string of \code{"great"}. + +Similarly, if we continue to swap the children of nodes \code{"eat"} and \code{"at"}, it produces a scrambled string \code{"rgtae"}. +\begin{Code} + rgtae + / \ + rg tae + / \ / \ +r g ta e + / \ + t a +\end{Code} + +We say that \code{"rgtae"} is a scrambled string of \code{"great"}. + +Given two strings $s1$ and $s2$ of the same length, determine if $s2$ is a scrambled string of $s1$. + + +\subsubsection{分析} +首先想到的是递归(即深搜),对两个string进行分割,然后比较四对字符串。代码虽然简单,但是复杂度比较高。有两种加速策略,一种是剪枝,提前返回;一种是加缓存,缓存中间结果,即memorization(翻译为记忆化搜索)。 + +剪枝可以五花八门,要充分观察,充分利用信息,找到能让节点提前返回的条件。例如,判断两个字符串是否互为scamble,至少要求每个字符在两个字符串中出现的次数要相等,如果不相等则返回false。 + +加缓存,可以用数组或HashMap。本题维数较高,用HashMap,\fn{map}和\fn{unordered_map}均可。 + +既然可以用记忆化搜索,这题也一定可以用动规。设状态为\fn{f[n][i][j]},表示长度为$n$,起点为\fn{s1[i]}和起点为\fn{s2[j]}两个字符串是否互为scramble,则状态转移方程为 +\begin{Code} +f[n][i][j]} = (f[k][i][j] && f[n-k][i+k][j+k]) + || (f[k][i][j+n-k] && f[n-k][i+k][j]) +\end{Code} + + +\subsubsection{递归} + +\begin{Code} +// LeetCode, Interleaving String +// 递归,会超时,仅用来帮助理解 +// 时间复杂度O(n^6),空间复杂度O(1) +class Solution { +public: + bool isScramble(string s1, string s2) { + return isScramble(s1.begin(), s1.end(), s2.begin()); + } +private: + typedef string::iterator Iterator; + bool isScramble(Iterator first1, Iterator last1, Iterator first2) { + auto length = distance(first1, last1); + auto last2 = next(first2, length); + + if (length == 1) return *first1 == *first2; + + for (int i = 1; i < length; ++i) + if ((isScramble(first1, first1 + i, first2) + && isScramble(first1 + i, last1, first2 + i)) + || (isScramble(first1, first1 + i, last2 - i) + && isScramble(first1 + i, last1, first2))) + return true; + + return false; + } +}; +\end{Code} + + +\subsubsection{动规} +\begin{Code} +// LeetCode, Interleaving String +// 动规,时间复杂度O(n^3),空间复杂度O(n^3) +class Solution { +public: + bool isScramble(string s1, string s2) { + const int N = s1.size(); + if (N != s2.size()) return false; + + // f[n][i][j],表示长度为n,起点为s1[i]和 + // 起点为s2[j]两个字符串是否互为scramble + bool f[N + 1][N][N]; + fill_n(&f[0][0][0], (N + 1) * N * N, false); + + for (int i = 0; i < N; i++) + for (int j = 0; j < N; j++) + f[1][i][j] = s1[i] == s2[j]; + + for (int n = 1; n <= N; ++n) { + for (int i = 0; i + n <= N; ++i) { + for (int j = 0; j + n <= N; ++j) { + for (int k = 1; k < n; ++k) { + if ((f[k][i][j] && f[n - k][i + k][j + k]) || + (f[k][i][j + n - k] && f[n - k][i + k][j])) { + f[n][i][j] = true; + break; + } + } + } + } + } + return f[N][0][0]; + } +}; +\end{Code} + + +\subsubsection{递归+剪枝} +\begin{Code} +// LeetCode, Interleaving String +// 递归+剪枝 +// 时间复杂度O(n^6),空间复杂度O(1) +class Solution { +public: + bool isScramble(string s1, string s2) { + return isScramble(s1.begin(), s1.end(), s2.begin()); + } +private: + typedef string::iterator Iterator; + bool isScramble(Iterator first1, Iterator last1, Iterator first2) { + auto length = distance(first1, last1); + auto last2 = next(first2, length); + if (length == 1) return *first1 == *first2; + + // 剪枝,提前返回 + int A[26]; // 每个字符的计数器 + fill(A, A + 26, 0); + for(int i = 0; i < length; i++) A[*(first1+i)-'a']++; + for(int i = 0; i < length; i++) A[*(first2+i)-'a']--; + for(int i = 0; i < 26; i++) if (A[i] != 0) return false; + + for (int i = 1; i < length; ++i) + if ((isScramble(first1, first1 + i, first2) + && isScramble(first1 + i, last1, first2 + i)) + || (isScramble(first1, first1 + i, last2 - i) + && isScramble(first1 + i, last1, first2))) + return true; + + return false; + } +}; +\end{Code} + + +\subsubsection{备忘录法} +\begin{Code} +// LeetCode, Interleaving String +// 递归+map做cache +// 时间复杂度O(n^3),空间复杂度O(n^3) +class Solution { +public: + bool isScramble(string s1, string s2) { + cache.clear(); + return isScramble(s1.begin(), s1.end(), s2.begin()); + } +private: + typedef string::const_iterator Iterator; + map, bool> cache; + + bool isScramble(Iterator first1, Iterator last1, Iterator first2) { + auto length = distance(first1, last1); + auto last2 = next(first2, length); + + if (length == 1) return *first1 == *first2; + + for (int i = 1; i < length; ++i) + if ((getOrUpdate(first1, first1 + i, first2) + && getOrUpdate(first1 + i, last1, first2 + i)) + || (getOrUpdate(first1, first1 + i, last2 - i) + && getOrUpdate(first1 + i, last1, first2))) + return true; + + return false; + } + + bool getOrUpdate(Iterator first1, Iterator last1, Iterator first2) { + auto key = make_tuple(first1, last1, first2); + auto pos = cache.find(key); + + return (pos != cache.end()) ? + pos->second : (cache[key] = isScramble(first1, last1, first2)); + } +}; +\end{Code} + + +\subsubsection{备忘录法} +\begin{Code} +typedef string::const_iterator Iterator; +typedef tuple Key; +// 定制一个哈希函数 +namespace std { +template<> struct hash { + size_t operator()(const Key & x) const { + Iterator first1, last1, first2; + tie(first1, last1, first2) = x; + + int result = *first1; + result = result * 31 + *last1; + result = result * 31 + *first2; + result = result * 31 + *(next(first2, distance(first1, last1)-1)); + return result; + } +}; +} + +// LeetCode, Interleaving String +// 递归+unordered_map做cache,比map快 +// 时间复杂度O(n^3),空间复杂度O(n^3) +class Solution { +public: + unordered_map cache; + + bool isScramble(string s1, string s2) { + cache.clear(); + return isScramble(s1.begin(), s1.end(), s2.begin()); + } + + bool isScramble(Iterator first1, Iterator last1, Iterator first2) { + auto length = distance(first1, last1); + auto last2 = next(first2, length); + + if (length == 1) + return *first1 == *first2; + + for (int i = 1; i < length; ++i) + if ((getOrUpdate(first1, first1 + i, first2) + && getOrUpdate(first1 + i, last1, first2 + i)) + || (getOrUpdate(first1, first1 + i, last2 - i) + && getOrUpdate(first1 + i, last1, first2))) + return true; + + return false; + } + + bool getOrUpdate(Iterator first1, Iterator last1, Iterator first2) { + auto key = make_tuple(first1, last1, first2); + auto pos = cache.find(key); + + return (pos != cache.end()) ? + pos->second : (cache[key] = isScramble(first1, last1, first2)); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{Minimum Path Sum} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:minimum-path-sum} + + +\subsubsection{描述} +Given a $m \times n$ grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path. + +Note: You can only move either down or right at any point in time + + +\subsubsection{分析} +跟 Unique Paths (见 \S \ref{sec:unique-paths}) 很类似。 + +设状态为\fn{f[i][j]},表示从起点$(0,0)$到达$(i,j)$的最小路径和,则状态转移方程为: +\begin{Code} +f[i][j]=min(f[i-1][j], f[i][j-1])+grid[i][j] +\end{Code} + + +\subsubsection{备忘录法} +\begin{Code} +// LeetCode, Minimum Path Sum +// 备忘录法 +class Solution { +public: + int minPathSum(vector > &grid) { + const int m = grid.size(); + const int n = grid[0].size(); + this->f = vector >(m, vector(n, -1)); + return dfs(grid, m-1, n-1); + } +private: + vector > f; // 缓存 + + int dfs(const vector > &grid, int x, int y) { + if (x < 0 || y < 0) return INT_MAX; // 越界,终止条件,注意,不是0 + + if (x == 0 && y == 0) return grid[0][0]; // 回到起点,收敛条件 + + return min(getOrUpdate(grid, x - 1, y), + getOrUpdate(grid, x, y - 1)) + grid[x][y]; + } + + int getOrUpdate(const vector > &grid, int x, int y) { + if (x < 0 || y < 0) return INT_MAX; // 越界,注意,不是0 + if (f[x][y] >= 0) return f[x][y]; + else return f[x][y] = dfs(grid, x, y); + } +}; +\end{Code} + + +\subsubsection{动规} +\begin{Code} +// LeetCode, Minimum Path Sum +// 二维动规 +class Solution { +public: + int minPathSum(vector > &grid) { + if (grid.size() == 0) return 0; + const int m = grid.size(); + const int n = grid[0].size(); + + int f[m][n]; + f[0][0] = grid[0][0]; + for (int i = 1; i < m; i++) { + f[i][0] = f[i - 1][0] + grid[i][0]; + } + for (int i = 1; i < n; i++) { + f[0][i] = f[0][i - 1] + grid[0][i]; + } + + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + f[i][j] = min(f[i - 1][j], f[i][j - 1]) + grid[i][j]; + } + } + return f[m - 1][n - 1]; + } +}; +\end{Code} + + +\subsubsection{动规+滚动数组} +\begin{Code} +// LeetCode, Minimum Path Sum +// 二维动规+滚动数组 +class Solution { +public: + int minPathSum(vector > &grid) { + const int m = grid.size(); + const int n = grid[0].size(); + + int f[n]; + fill(f, f+n, INT_MAX); // 初始值是 INT_MAX,因为后面用了min函数。 + f[0] = 0; + + for (int i = 0; i < m; i++) { + f[0] += grid[i][0]; + for (int j = 1; j < n; j++) { + // 左边的f[j],表示更新后的f[j],与公式中的f[i[[j]对应 + // 右边的f[j],表示老的f[j],与公式中的f[i-1][j]对应 + f[j] = min(f[j - 1], f[j]) + grid[i][j]; + } + } + return f[n - 1]; + } +}; +\end{Code} + +\subsubsection{相关题目} +\begindot +\item Unique Paths, 见 \S \ref{sec:unique-paths} +\item Unique Paths II, 见 \S \ref{sec:unique-paths-ii} +\myenddot + + +\section{Edit Distance} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:edit-distance} + + +\subsubsection{描述} +Given two words \fn{word1} and \fn{word2}, find the minimum number of steps required to convert \fn{word1} to \fn{word2}. (each operation is counted as 1 step.) + +You have the following 3 operations permitted on a word: +\begindot +\item Insert a character +\item Delete a character +\item Replace a character +\myenddot + + +\subsubsection{分析} +设状态为\fn{f[i][j]},表示\fn{A[0,i]}和\fn{B[0,j]}之间的最小编辑距离。设\fn{A[0,i]}的形式是\fn{str1c},\fn{B[0,j]}的形式是\fn{str2d}, +\begin{enumerate} +\item 如果\fn{c==d},则\fn{f[i][j]=f[i-1][j-1]}; +\item 如果\fn{c!=d}, + \begin{enumerate} + \item 如果将c替换成d,则\fn{f[i][j]=f[i-1][j-1]+1}; + \item 如果在c后面添加一个d,则\fn{f[i][j]=f[i][j-1]+1}; + \item 如果将c删除,则\fn{f[i][j]=f[i-1][j]+1}; + \end{enumerate} +\end{enumerate} + + +\subsubsection{动规} +\begin{Code} +// LeetCode, Edit Distance +// 二维动规,时间复杂度O(n*m),空间复杂度O(n*m) +class Solution { +public: + int minDistance(const string &word1, const string &word2) { + const size_t n = word1.size(); + const size_t m = word2.size(); + // 长度为n的字符串,有n+1个隔板 + int f[n + 1][m + 1]; + for (size_t i = 0; i <= n; i++) + f[i][0] = i; + for (size_t j = 0; j <= m; j++) + f[0][j] = j; + + for (size_t i = 1; i <= n; i++) { + for (size_t j = 1; j <= m; j++) { + if (word1[i - 1] == word2[j - 1]) + f[i][j] = f[i - 1][j - 1]; + else { + int mn = min(f[i - 1][j], f[i][j - 1]); + f[i][j] = 1 + min(f[i - 1][j - 1], mn); + } + } + } + return f[n][m]; + } +}; +\end{Code} + + +\subsubsection{动规+滚动数组} +\begin{Code} +// LeetCode, Edit Distance +// 二维动规+滚动数组 +// 时间复杂度O(n*m),空间复杂度O(n) +class Solution { +public: + int minDistance(const string &word1, const string &word2) { + if (word1.length() < word2.length()) + return minDistance(word2, word1); + + int f[word2.length() + 1]; + int upper_left = 0; // 额外用一个变量记录f[i-1][j-1] + + for (size_t i = 0; i <= word2.size(); ++i) + f[i] = i; + + for (size_t i = 1; i <= word1.size(); ++i) { + upper_left = f[0]; + f[0] = i; + + for (size_t j = 1; j <= word2.size(); ++j) { + int upper = f[j]; + + if (word1[i - 1] == word2[j - 1]) + f[j] = upper_left; + else + f[j] = 1 + min(upper_left, min(f[j], f[j - 1])); + + upper_left = upper; + } + } + + return f[word2.length()]; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{Decode Ways} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:decode-ways} + + +\subsubsection{描述} +A message containing letters from \fn{A-Z} is being encoded to numbers using the following mapping: +\begin{Code} +'A' -> 1 +'B' -> 2 +... +'Z' -> 26 +\end{Code} + +Given an encoded message containing digits, determine the total number of ways to decode it. + +For example, +Given encoded message \fn{"12"}, it could be decoded as \fn{"AB"} (1 2) or \fn{"L"} (12). + +The number of ways decoding \fn{"12"} is 2. + + +\subsubsection{分析} +跟 Climbing Stairs (见 \S \ref{sec:climbing-stairs})很类似,不过多加一些判断逻辑。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Decode Ways +// 动规,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int numDecodings(const string &s) { + if (s.empty() || s[0] == '0') return 0; + + int prev = 0; + int cur = 1; + // 长度为n的字符串,有 n+1个阶梯 + for (size_t i = 1; i <= s.size(); ++i) { + if (s[i-1] == '0') cur = 0; + + if (i < 2 || !(s[i - 2] == '1' || + (s[i - 2] == '2' && s[i - 1] <= '6'))) + prev = 0; + + int tmp = cur; + cur = prev + cur; + prev = tmp; + } + return cur; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Climbing Stairs, 见 \S \ref{sec:climbing-stairs} +\myenddot + + +\section{Distinct Subsequences} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:distinct-subsequences} + + +\subsubsection{描述} +Given a string $S$ and a string $T$, count the number of distinct subsequences of $T$ in $S$. + +A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, \fn{"ACE"} is a subsequence of \fn{"ABCDE"} while \fn{"AEC"} is not). + +Here is an example: +$S$ = \fn{"rabbbit"}, $T$ = \fn{"rabbit"} + +Return 3. + + +\subsubsection{分析} +设状态为$f(i,j)$,表示\fn{T[0,j]}在\fn{S[0,i]}里出现的次数。首先,无论\fn{S[i]}和\fn{T[j]}是否相等,若不使用\fn{S[i]},则$f(i,j)=f(i-1,j)$;若\fn{S[i]==T[j]},则可以使用\fn{S[i]},此时$f(i,j)=f(i-1,j)+f(i-1, j-1)$。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Distinct Subsequences +// 二维动规+滚动数组 +// 时间复杂度O(m*n),空间复杂度O(n) +class Solution { +public: + int numDistinct(const string &S, const string &T) { + vector f(T.size() + 1); + f[0] = 1; + for (int i = 0; i < S.size(); ++i) { + for (int j = T.size() - 1; j >= 0; --j) { + f[j + 1] += S[i] == T[j] ? f[j] : 0; + } + } + + return f[T.size()]; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{Word Break} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:word-break} + + +\subsubsection{描述} +Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words. + +For example, given \\ +s = \fn{"leetcode"},\\ +dict = \fn{["leet", "code"]}. + +Return true because \fn{"leetcode"} can be segmented as \fn{"leet code"}. + + +\subsubsection{分析} +设状态为$f(i)$,表示\fn{s[0,i]}是否可以分词,则状态转移方程为 +$$ +f(i) = any\_of(f(j) \&\& s[j+1,i] \in dict), 0 \leq j < i +$$ + + +\subsubsection{深搜} +\begin{Code} +// LeetCode, Word Break +// 深搜,超时 +// 时间复杂度O(2^n),空间复杂度O(n) +class Solution { +public: + bool wordBreak(string s, unordered_set &dict) { + return dfs(s, dict, 0, 0); + } +private: + static bool dfs(const string &s, unordered_set &dict, + size_t start, size_t cur) { + if (cur == s.size()) { + return dict.find(s.substr(start, cur-start+1)) != dict.end(); + } + if (dfs(s, dict, start, cur+1)) return true; + if (dict.find(s.substr(start, cur-start+1)) != dict.end()) + if (dfs(s, dict, cur+1, cur+1)) return true; + return false; + } +}; +\end{Code} + + +\subsubsection{动规} +\begin{Code} +// LeetCode, Word Break +// 动规,时间复杂度O(n^2),空间复杂度O(n) +class Solution { +public: + bool wordBreak(string s, unordered_set &dict) { + // 长度为n的字符串有n+1个隔板 + vector f(s.size() + 1, false); + f[0] = true; // 空字符串 + for (int i = 1; i <= s.size(); ++i) { + for (int j = i - 1; j >= 0; --j) { + if (f[j] && dict.find(s.substr(j, i - j)) != dict.end()) { + f[i] = true; + break; + } + } + } + return f[s.size()]; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Word Break II, 见 \S \ref{sec:word-break-ii} +\myenddot + + +\section{Word Break II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:word-break-ii} + + +\subsubsection{描述} +Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each word is a valid dictionary word. + +Return all such possible sentences. + +For example, given \\ +s = \fn{"catsanddog"}, \\ +dict = \fn{["cat", "cats", "and", "sand", "dog"]}. + +A solution is \fn{["cats and dog", "cat sand dog"]}. + + +\subsubsection{分析} +在上一题的基础上,要返回解本身。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Word Break II +// 动规,时间复杂度O(n^2),空间复杂度O(n^2) +class Solution { +public: + vector wordBreak(string s, unordered_set &dict) { + // 长度为n的字符串有n+1个隔板 + vector f(s.length() + 1, false); + // prev[i][j]为true,表示s[j, i)是一个合法单词,可以从j处切开 + // 第一行未用 + vector > prev(s.length() + 1, vector(s.length())); + f[0] = true; + for (size_t i = 1; i <= s.length(); ++i) { + for (int j = i - 1; j >= 0; --j) { + if (f[j] && dict.find(s.substr(j, i - j)) != dict.end()) { + f[i] = true; + prev[i][j] = true; + } + } + } + vector result; + vector path; + gen_path(s, prev, s.length(), path, result); + return result; + + } +private: + // DFS遍历树,生成路径 + void gen_path(const string &s, const vector > &prev, + int cur, vector &path, vector &result) { + if (cur == 0) { + string tmp; + for (auto iter = path.crbegin(); iter != path.crend(); ++iter) + tmp += *iter + " "; + tmp.erase(tmp.end() - 1); + result.push_back(tmp); + } + for (size_t i = 0; i < s.size(); ++i) { + if (prev[cur][i]) { + path.push_back(s.substr(i, cur - i)); + gen_path(s, prev, i, path, result); + path.pop_back(); + } + } + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Word Break, 见 \S \ref{sec:word-break} +\myenddot diff --git a/leetcode/C++/chapGraph.tex b/leetcode/C++/chapGraph.tex new file mode 100644 index 00000000..c3f30918 --- /dev/null +++ b/leetcode/C++/chapGraph.tex @@ -0,0 +1,123 @@ +\chapter{图} + +无向图的节点定义如下: +\begin{Code} +// 无向图的节点 +struct UndirectedGraphNode { + int label; + vector neighbors; + UndirectedGraphNode(int x) : label(x) {}; +}; +\end{Code} + + +\section{Clone Graph} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:clone-graph} + + +\subsubsection{描述} +Clone an undirected graph. Each node in the graph contains a \code{label} and a list of its \code{neighbours}. + + +OJ's undirected graph serialization: +Nodes are labeled uniquely. + +We use \code{\#} as a separator for each node, and \code{,} as a separator for node label and each neighbour of the node. +As an example, consider the serialized graph \code{\{0,1,2\#1,2\#2,2\}}. + +The graph has a total of three nodes, and therefore contains three parts as separated by \code{\#}. +\begin{enumerate} +\item First node is labeled as 0. Connect node 0 to both nodes 1 and 2. +\item Second node is labeled as 1. Connect node 1 to node 2. +\item Third node is labeled as 2. Connect node 2 to node 2 (itself), thus forming a self-cycle. +\end{enumerate} + +Visually, the graph looks like the following: +\begin{Code} + 1 + / \ + / \ + 0 --- 2 + / \ + \_/ +\end{Code} + + +\subsubsection{分析} +广度优先遍历或深度优先遍历都可以。 + + +\subsubsection{DFS} +\begin{Code} +// LeetCode, Clone Graph +// DFS,时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + UndirectedGraphNode *cloneGraph(const UndirectedGraphNode *node) { + if(node == nullptr) return nullptr; + // key is original node,value is copied node + unordered_map copied; + clone(node, copied); + return copied[node]; + } +private: + // DFS + static UndirectedGraphNode* clone(const UndirectedGraphNode *node, + unordered_map &copied) { + // a copy already exists + if (copied.find(node) != copied.end()) return copied[node]; + + UndirectedGraphNode *new_node = new UndirectedGraphNode(node->label); + copied[node] = new_node; + for (auto nbr : node->neighbors) + new_node->neighbors.push_back(clone(nbr, copied)); + return new_node; + } +}; +\end{Code} + + +\subsubsection{BFS} +\begin{Code} +// LeetCode, Clone Graph +// BFS,时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + UndirectedGraphNode *cloneGraph(const UndirectedGraphNode *node) { + if (node == nullptr) return nullptr; + // key is original node,value is copied node + unordered_map copied; + // each node in queue is already copied itself + // but neighbors are not copied yet + queue q; + q.push(node); + copied[node] = new UndirectedGraphNode(node->label); + while (!q.empty()) { + const UndirectedGraphNode *cur = q.front(); + q.pop(); + for (auto nbr : cur->neighbors) { + // a copy already exists + if (copied.find(nbr) != copied.end()) { + copied[cur]->neighbors.push_back(copied[nbr]); + } else { + UndirectedGraphNode *new_node = + new UndirectedGraphNode(nbr->label); + copied[nbr] = new_node; + copied[cur]->neighbors.push_back(new_node); + q.push(nbr); + } + } + } + return copied[node]; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot diff --git a/leetcode/C++/chapGreedy.tex b/leetcode/C++/chapGreedy.tex new file mode 100644 index 00000000..86b67e3a --- /dev/null +++ b/leetcode/C++/chapGreedy.tex @@ -0,0 +1,356 @@ +\chapter{贪心法} + + +\section{Jump Game} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:jump-game} + + +\subsubsection{描述} +Given an array of non-negative integers, you are initially positioned at the first index of the array. + +Each element in the array represents your maximum jump length at that position. + +Determine if you are able to reach the last index. + +For example: + +\code{A = [2,3,1,1,4]}, return true. + +\code{A = [3,2,1,0,4]}, return false. + + +\subsubsection{分析} +由于每层最多可以跳\fn{A[i]}步,也可以跳0或1步,因此如果能到达最高层,则说明每一层都可以到达。有了这个条件,说明可以用贪心法。 + +思路一:正向,从0出发,一层一层网上跳,看最后能不能超过最高层,能超过,说明能到达,否则不能到达。 + +思路二:逆向,从最高层下楼梯,一层一层下降,看最后能不能下降到第0层。 + +思路三:如果不敢用贪心,可以用动规,设状态为\fn{f[i]},表示从第0层出发,走到\fn{A[i]}时剩余的最大步数,则状态转移方程为: +$$ +f[i] = max(f[i-1], A[i-1])-1, i > 0 +$$ + + +\subsubsection{代码1} +\begin{Code} +// LeetCode, Jump Game +// 思路1,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + bool canJump(int A[], int n) { + int reach = 1; // 最右能跳到哪里 + for (int i = 0; i < reach && reach < n; ++i) + reach = max(reach, i + 1 + A[i]); + return reach >= n; + } +}; +\end{Code} + + +\subsubsection{代码2} +\begin{Code} +// LeetCode, Jump Game +// 思路2,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + bool canJump (int A[], int n) { + if (n == 0) return true; + // 逆向下楼梯,最左能下降到第几层 + int left_most = n - 1; + + for (int i = n - 2; i >= 0; --i) + if (i + A[i] >= left_most) + left_most = i; + + return left_most == 0; + } +}; +\end{Code} + + +\subsubsection{代码3} +\begin{Code} +// LeetCode, Jump Game +// 思路三,动规,时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + bool canJump(int A[], int n) { + vector f(n, 0); + f[0] = 0; + for (int i = 1; i < n; i++) { + f[i] = max(f[i - 1], A[i - 1]) - 1; + if (f[i] < 0) return false;; + } + return f[n - 1] >= 0; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Jump Game II ,见 \S \ref{sec:jump-game-ii} +\myenddot + + +\section{Jump Game II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:jump-game-ii} + + +\subsubsection{描述} +Given an array of non-negative integers, you are initially positioned at the first index of the array. + +Each element in the array represents your maximum jump length at that position. + +Your goal is to reach the last index in the minimum number of jumps. + +For example: +Given array \code{A = [2,3,1,1,4]} + +The minimum number of jumps to reach the last index is 2. (Jump 1 step from index 0 to 1, then 3 steps to the last index.) + + +\subsubsection{分析} +贪心法。 + + +\subsubsection{代码1} +\begin{Code} +// LeetCode, Jump Game II +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int jump(int A[], int n) { + int step = 0; // 最小步数 + int left = 0; + int right = 0; // [left, right]是当前能覆盖的区间 + if (n == 1) return 0; + + while (left <= right) { // 尝试从每一层跳最远 + ++step; + const int old_right = right; + for (int i = left; i <= old_right; ++i) { + int new_right = i + A[i]; + if (new_right >= n - 1) return step; + + if (new_right > right) right = new_right; + } + left = old_right + 1; + } + return 0; + } +}; +\end{Code} + + +\subsubsection{代码2} +\begin{Code} +// LeetCode, Jump Game II +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int jump(int A[], int n) { + int result = 0; + // the maximum distance that has been reached + int last = 0; + // the maximum distance that can be reached by using "ret+1" steps + int cur = 0; + for (int i = 0; i < n; ++i) { + if (i > last) { + last = cur; + ++result; + } + cur = max(cur, i + A[i]); + } + + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Jump Game ,见 \S \ref{sec:jump-game} +\myenddot + + +\section{Best Time to Buy and Sell Stock} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:best-time-to-buy-and-sell-stock} + + +\subsubsection{描述} +Say you have an array for which the i-th element is the price of a given stock on day i. + +If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit. + + +\subsubsection{分析} +贪心法,分别找到价格最低和最高的一天,低进高出,注意最低的一天要在最高的一天之前。 + +把原始价格序列变成差分序列,本题也可以做是最大$m$子段和,$m=1$。 + +\subsubsection{代码} +\begin{Code} +// LeetCode, Best Time to Buy and Sell Stock +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int maxProfit(vector &prices) { + if (prices.size() < 2) return 0; + int profit = 0; // 差价,也就是利润 + int cur_min = prices[0]; // 当前最小 + + for (int i = 1; i < prices.size(); i++) { + profit = max(profit, prices[i] - cur_min); + cur_min = min(cur_min, prices[i]); + } + return profit; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Best Time to Buy and Sell Stock II,见 \S \ref{sec:best-time-to-buy-and-sell-stock-ii} +\item Best Time to Buy and Sell Stock III,见 \S \ref{sec:best-time-to-buy-and-sell-stock-iii} +\myenddot + + +\section{Best Time to Buy and Sell Stock II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:best-time-to-buy-and-sell-stock-ii} + + +\subsubsection{描述} +Say you have an array for which the i-th element is the price of a given stock on day i. + +Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). + + +\subsubsection{分析} +贪心法,低进高出,把所有正的价格差价相加起来。 + +把原始价格序列变成差分序列,本题也可以做是最大$m$子段和,$m=$数组长度。 + +\subsubsection{代码} +\begin{Code} +// LeetCode, Best Time to Buy and Sell Stock II +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int maxProfit(vector &prices) { + int sum = 0; + for (int i = 1; i < prices.size(); i++) { + int diff = prices[i] - prices[i - 1]; + if (diff > 0) sum += diff; + } + return sum; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Best Time to Buy and Sell Stock,见 \S \ref{sec:best-time-to-buy-and-sell-stock} +\item Best Time to Buy and Sell Stock III,见 \S \ref{sec:best-time-to-buy-and-sell-stock-iii} +\myenddot + + +\section{Longest Substring Without Repeating Characters} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:longest-substring-without-repeating-characters} + + +\subsubsection{描述} +Given a string, find the length of the longest substring without repeating characters. For example, the longest substring without repeating letters for \code{"abcabcbb"} is \code{"abc"}, which the length is 3. For \code{"bbbbb"} the longest substring is \code{"b"}, with the length of 1. + + +\subsubsection{分析} +假设子串里含有重复字符,则父串一定含有重复字符,单个子问题就可以决定父问题,因此可以用贪心法。跟动规不同,动规里,单个子问题只能影响父问题,不足以决定父问题。 + +从左往右扫描,当遇到重复字母时,以上一个重复字母的\fn{index+1},作为新的搜索起始位置,直到最后一个字母,复杂度是$O(n)$。如图~\ref{fig:longest-substring-without-repeating-characters}所示。 + +\begin{center} +\includegraphics[width=300pt]{longest-substring-without-repeating-characters.png}\\ +\figcaption{不含重复字符的最长子串}\label{fig:longest-substring-without-repeating-characters} +\end{center} + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Longest Substring Without Repeating Characters +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int lengthOfLongestSubstring(string s) { + const int ASCII_MAX = 26; + int last[ASCII_MAX]; // 记录字符上次出现过的位置 + int start = 0; // 记录当前子串的起始位置 + + fill(last, last + ASCII_MAX, -1); // 0也是有效位置,因此初始化为-1 + int max_len = 0; + for (int i = 0; i < s.size(); i++) { + if (last[s[i] - 'a'] >= start) { + max_len = max(i - start, max_len); + start = last[s[i] - 'a'] + 1; + } + last[s[i] - 'a'] = i; + } + return max((int)s.size() - start, max_len); // 别忘了最后一次,例如"abcd" + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{Container With Most Water} +\label{sec:container-with-most-water} + + +\subsubsection{描述} +Given $n$ non-negative integers $a_1, a_2, ..., a_n$, where each represents a point at coordinate $(i, a_i)$. n vertical lines are drawn such that the two endpoints of line $i$ is at $(i, a_i)$ and $(i, 0)$. Find two lines, which together with x-axis forms a container, such that the container contains the most water. + +Note: You may not slant the container. + + +\subsubsection{分析} +每个容器的面积,取决于最短的木板。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Container With Most Water +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int maxArea(vector &height) { + int start = 0; + int end = height.size() - 1; + int result = INT_MIN; + while (start < end) { + int area = min(height[end], height[start]) * (end - start); + result = max(result, area); + if (height[start] <= height[end]) { + start++; + } else { + end--; + } + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Trapping Rain Water, 见 \S \ref{sec:trapping-rain-water} +\item Largest Rectangle in Histogram, 见 \S \ref{sec:largest-rectangle-in-histogram} +\myenddot diff --git a/leetcode/C++/chapImplement.tex b/leetcode/C++/chapImplement.tex new file mode 100644 index 00000000..6c0aeab8 --- /dev/null +++ b/leetcode/C++/chapImplement.tex @@ -0,0 +1,1188 @@ +\chapter{细节实现题} +这类题目不考特定的算法,纯粹考察写代码的熟练度。 + + +\section{Reverse Integer} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:reverse-integer} + + +\subsubsection{描述} +Reverse digits of an integer. + +Example1: x = 123, return 321 + +Example2: x = -123, return -321 + + +\textbf{Have you thought about this?} + +Here are some good questions to ask before coding. Bonus points for you if you have already thought through this! + +If the integer's last digit is 0, what should the output be? ie, cases such as 10, 100. + +Did you notice that the reversed integer might overflow? Assume the input is a 32-bit integer, then the reverse of 1000000003 overflows. How should you handle such cases? + +Throw an exception? Good, but what if throwing an exception is not an option? You would then have to re-design the function (ie, add an extra parameter). + + +\subsubsection{分析} +短小精悍的题,代码也可以写的很短小。 + + +\subsubsection{代码} +\begin{Code} +//LeetCode, Reverse Integer +// 时间复杂度O(logn),空间复杂度O(1) +class Solution { +public: + int reverse (int x) { + int r = 0; + + for (; x; x /= 10) + r = r * 10 + x % 10; + + return r; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Palindrome Number, 见 \S \ref{sec:palindrome-number} +\myenddot + + +\section{Palindrome Number} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:palindrome-number} + + +\subsubsection{描述} +Determine whether an integer is a palindrome. Do this without extra space. + +\textbf{Some hints:} + +Could negative integers be palindromes? (ie, -1) + +If you are thinking of converting the integer to string, note the restriction of using extra space. + +You could also try reversing an integer. However, if you have solved the problem "Reverse Integer", you know that the reversed integer might overflow. How would you handle such case? + +There is a more generic way of solving this problem. + + +\subsubsection{分析} +首先想到,可以利用上一题,将整数反转,然后与原来的整数比较,是否相等,相等则为 Palindrome 的。可是 reverse()会溢出。 + +正确的解法是,不断地取第一位和最后一位(10进制下)进行比较,相等则取第二位和倒数第二位,直到完成比较或者中途找到了不一致的位。 + + +\subsubsection{代码} +\begin{Code} +//LeetCode, Palindrome Number +// 时间复杂度O(1),空间复杂度O(1) +class Solution { +public: + bool isPalindrome(int x) { + if (x < 0) return false; + int d = 1; // divisor + while (x / d >= 10) d *= 10; + + while (x > 0) { + int q = x / d; // quotient + int r = x % 10; // remainder + if (q != r) return false; + x = x % d / 10; + d /= 100; + } + return true; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Reverse Integer, 见 \S \ref{sec:reverse-integer} +\item Valid Palindrome, 见 \S \ref{sec:valid-palindrome} +\myenddot + + +\section{Insert Interval} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:insert-interval} + + +\subsubsection{描述} +Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary). + +You may assume that the intervals were initially sorted according to their start times. + +Example 1: +Given intervals \code{[1,3],[6,9]}, insert and merge \code{[2,5]} in as \code{[1,5],[6,9]}. + +Example 2: +Given \code{[1,2],[3,5],[6,7],[8,10],[12,16]}, insert and merge \code{[4,9]} in as \code{[1,2],[3,10],[12,16]}. + +This is because the new interval \code{[4,9]} overlaps with \code{[3,5],[6,7],[8,10]}. + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +struct Interval { + int start; + int end; + Interval() : start(0), end(0) { } + Interval(int s, int e) : start(s), end(e) { } +}; + +//LeetCode, Insert Interval +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + vector insert(vector &intervals, Interval newInterval) { + vector::iterator it = intervals.begin(); + while (it != intervals.end()) { + if (newInterval.end < it->start) { + intervals.insert(it, newInterval); + return intervals; + } else if (newInterval.start > it->end) { + it++; + continue; + } else { + newInterval.start = min(newInterval.start, it->start); + newInterval.end = max(newInterval.end, it->end); + it = intervals.erase(it); + } + } + intervals.insert(intervals.end(), newInterval); + return intervals; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item Merge Intervals,见 \S \ref{sec:merge-intervals} +\myenddot + + +\section{Merge Intervals} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:merge-intervals} + + +\subsubsection{描述} +Given a collection of intervals, merge all overlapping intervals. + +For example, +Given \code{[1,3],[2,6],[8,10],[15,18]}, +return \code{[1,6],[8,10],[15,18]} + + +\subsubsection{分析} +复用一下Insert Intervals的解法即可,创建一个新的interval集合,然后每次从旧的里面取一个interval出来,然后插入到新的集合中。 + + +\subsubsection{代码} +\begin{Code} +struct Interval { + int start; + int end; + Interval() : start(0), end(0) { } + Interval(int s, int e) : start(s), end(e) { } +}; + +//LeetCode, Merge Interval +//复用一下Insert Intervals的解法即可 +// 时间复杂度O(n1+n2+...),空间复杂度O(1) +class Solution { +public: + vector merge(vector &intervals) { + vector result; + for (int i = 0; i < intervals.size(); i++) { + insert(result, intervals[i]); + } + return result; + } +private: + vector insert(vector &intervals, Interval newInterval) { + vector::iterator it = intervals.begin(); + while (it != intervals.end()) { + if (newInterval.end < it->start) { + intervals.insert(it, newInterval); + return intervals; + } else if (newInterval.start > it->end) { + it++; + continue; + } else { + newInterval.start = min(newInterval.start, it->start); + newInterval.end = max(newInterval.end, it->end); + it = intervals.erase(it); + } + } + intervals.insert(intervals.end(), newInterval); + return intervals; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item Insert Interval,见 \S \ref{sec:insert-interval} +\myenddot + + +\section{Minimum Window Substring} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:minimum-window-substring} + + +\subsubsection{描述} +Given a string $S$ and a string $T$, find the minimum window in $S$ which will contain all the characters in $T$ in complexity $O(n)$. + +For example, \code{S = "ADOBECODEBANC", T = "ABC"} + +Minimum window is \code{"BANC"}. + +Note: +\begindot +\item If there is no such window in $S$ that covers all characters in $T$, return the emtpy string \code{""}. +\item If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in $S$. +\myenddot + + +\subsubsection{分析} +双指针,动态维护一个区间。尾指针不断往后扫,当扫到有一个窗口包含了所有$T$的字符后,然后再收缩头指针,直到不能再收缩为止。最后记录所有可能的情况中窗口最小的 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Minimum Window Substring +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + string minWindow(string S, string T) { + if (S.empty()) return ""; + if (S.size() < T.size()) return ""; + + const int ASCII_MAX = 256; + int appeared_count[ASCII_MAX]; + int expected_count[ASCII_MAX]; + fill(appeared_count, appeared_count + ASCII_MAX, 0); + fill(expected_count, expected_count + ASCII_MAX, 0); + + for (size_t i = 0; i < T.size(); i++) expected_count[T[i]]++; + + int minWidth = INT_MAX, min_start = 0; // 窗口大小,起点 + int wnd_start = 0; + int appeared = 0; // 完整包含了一个T + //尾指针不断往后扫 + for (size_t wnd_end = 0; wnd_end < S.size(); wnd_end++) { + if (expected_count[S[wnd_end]] > 0) { // this char is a part of T + appeared_count[S[wnd_end]]++; + if (appeared_count[S[wnd_end]] <= expected_count[S[wnd_end]]) + appeared++; + } + if (appeared == T.size()) { // 完整包含了一个T + // 收缩头指针 + while (appeared_count[S[wnd_start]] > expected_count[S[wnd_start]] + || expected_count[S[wnd_start]] == 0) { + appeared_count[S[wnd_start]]--; + wnd_start++; + } + if (minWidth > (wnd_end - wnd_start + 1)) { + minWidth = wnd_end - wnd_start + 1; + min_start = wnd_start; + } + } + } + + if (minWidth == INT_MAX) return ""; + else return S.substr(min_start, minWidth); + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item 无 +\myenddot + + +\section{Multiply Strings} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:multiply-strings} + + +\subsubsection{描述} +Given two numbers represented as strings, return multiplication of the numbers as a string. + +Note: The numbers can be arbitrarily large and are non-negative. + + +\subsubsection{分析} +高精度乘法。 + +常见的做法是将字符转化为一个int,一一对应,形成一个int数组。但是这样很浪费空间,一个int32的最大值是$2^{31}-1=2147483647$,可以与9个字符对应,由于有乘法,减半,则至少可以与4个字符一一对应。一个int64可以与9个字符对应。 + + +\subsubsection{代码1} +\begin{Code} +// LeetCode, Multiply Strings +// @author 连城 (http://weibo.com/lianchengzju) +// 一个字符对应一个int +// 时间复杂度O(n*m),空间复杂度O(n+m) +typedef vector bigint; + +bigint make_bigint(string const& repr) { + bigint n; + transform(repr.rbegin(), repr.rend(), back_inserter(n), + [](char c) { return c - '0'; }); + return n; +} + +string to_string(bigint const& n) { + string str; + transform(find_if(n.rbegin(), prev(n.rend()), + [](char c) { return c > '\0'; }), n.rend(), back_inserter(str), + [](char c) { return c + '0'; }); + return str; +} + +bigint operator*(bigint const& x, bigint const& y) { + bigint z(x.size() + y.size()); + + for (size_t i = 0; i < x.size(); ++i) + for (size_t j = 0; j < y.size(); ++j) { + z[i + j] += x[i] * y[j]; + z[i + j + 1] += z[i + j] / 10; + z[i + j] %= 10; + } + + return z; +} + +class Solution { +public: + string multiply(string num1, string num2) { + return to_string(make_bigint(num1) * make_bigint(num2)); + } +}; +\end{Code} + + +\subsubsection{代码2} +\begin{Code} +// LeetCode, Multiply Strings +// 9个字符对应一个int64_t +// 时间复杂度O(n*m/81),空间复杂度O((n+m)/9) +/** 大整数类. */ +class BigInt { +public: + /** + * @brief 构造函数,将字符串转化为大整数. + * @param[in] s 输入的字符串 + * @return 无 + */ + BigInt(string s) { + vector result; + result.reserve(s.size() / RADIX_LEN + 1); + + for (int i = s.size(); i > 0; i -= RADIX_LEN) { // [i-RADIX_LEN, i) + int temp = 0; + const int low = max(i - RADIX_LEN, 0); + for (int j = low; j < i; j++) { + temp = temp * 10 + s[j] - '0'; + } + result.push_back(temp); + } + elems = result; + } + /** + * @brief 将整数转化为字符串. + * @return 字符串 + */ + string toString() { + stringstream result; + bool started = false; // 用于跳过前导0 + for (auto i = elems.rbegin(); i != elems.rend(); i++) { + if (started) { // 如果多余的0已经都跳过,则输出 + result << setw(RADIX_LEN) << setfill('0') << *i; + } else { + result << *i; + started = true; // 碰到第一个非0的值,就说明多余的0已经都跳过 + } + } + + if (!started) return "0"; // 当x全为0时 + else return result.str(); + } + + /** + * @brief 大整数乘法. + * @param[in] x x + * @param[in] y y + * @return 大整数 + */ + static BigInt multiply(const BigInt &x, const BigInt &y) { + vector z(x.elems.size() + y.elems.size(), 0); + + for (size_t i = 0; i < y.elems.size(); i++) { + for (size_t j = 0; j < x.elems.size(); j++) { // 用y[i]去乘以x的各位 + // 两数第i, j位相乘,累加到结果的第i+j位 + z[i + j] += y.elems[i] * x.elems[j]; + + if (z[i + j] >= BIGINT_RADIX) { // 看是否要进位 + z[i + j + 1] += z[i + j] / BIGINT_RADIX; // 进位 + z[i + j] %= BIGINT_RADIX; + } + } + } + while (z.back() == 0) z.pop_back(); // 没有进位,去掉最高位的0 + return BigInt(z); + } + +private: + typedef long long int64_t; + /** 一个数组元素对应9个十进制位,即数组是亿进制的 + * 因为 1000000000 * 1000000000 没有超过 2^63-1 + */ + const static int BIGINT_RADIX = 1000000000; + const static int RADIX_LEN = 9; + /** 万进制整数. */ + vector elems; + BigInt(const vector num) : elems(num) {} +}; + + +class Solution { +public: + string multiply(string num1, string num2) { + BigInt x(num1); + BigInt y(num2); + return BigInt::multiply(x, y).toString(); + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item 无 +\myenddot + + +\section{Substring with Concatenation of All Words} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:substring-with-concatenation-of-all-words} + + +\subsubsection{描述} +You are given a string, $S$, and a list of words, $L$, that are all of the same length. Find all starting indices of substring(s) in $S$ that is a concatenation of each word in $L$ exactly once and without any intervening characters. + +For example, given: +\begin{Code} +S: "barfoothefoobarman" +L: ["foo", "bar"] +\end{Code} + +You should return the indices: \code{[0,9]}.(order does not matter). + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Substring with Concatenation of All Words +// 时间复杂度O(n*m),空间复杂度O(m) +class Solution { +public: + vector findSubstring(string s, vector& dict) { + size_t wordLength = dict.front().length(); + size_t catLength = wordLength * dict.size(); + vector result; + + if (s.length() < catLength) return result; + + unordered_map wordCount; + + for (auto const& word : dict) ++wordCount[word]; + + for (auto i = begin(s); i <= prev(end(s), catLength); ++i) { + unordered_map unused(wordCount); + + for (auto j = i; j != next(i, catLength); j += wordLength) { + auto pos = unused.find(string(j, next(j, wordLength))); + + if (pos == unused.end() || pos->second == 0) break; + + if (--pos->second == 0) unused.erase(pos); + } + + if (unused.size() == 0) result.push_back(distance(begin(s), i)); + } + + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item 无 +\myenddot + + +\section{Pascal's Triangle} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:pascals-triangle} + + +\subsubsection{描述} +Given $numRows$, generate the first $numRows$ of Pascal's triangle. + +For example, given $numRows = 5$, + +Return +\begin{Code} +[ + [1], + [1,1], + [1,2,1], + [1,3,3,1], + [1,4,6,4,1] +] +\end{Code} + + +\subsubsection{分析} +本题可以用队列,计算下一行时,给上一行左右各加一个0,然后下一行的每个元素,就等于左上角和右上角之和。 + +另一种思路,下一行第一个元素和最后一个元素赋值为1,中间的每个元素,等于上一行的左上角和右上角元素之和。 + + +\subsubsection{从左到右} +\begin{Code} +// LeetCode, Pascal's Triangle +// 时间复杂度O(n^2),空间复杂度O(n) +class Solution { +public: + vector > generate(int numRows) { + vector > result; + if(numRows == 0) return result; + + result.push_back(vector(1,1)); //first row + + for(int i = 2; i <= numRows; ++i) { + vector current(i,1); // 本行 + const vector &prev = result[i-2]; // 上一行 + + for(int j = 1; j < i - 1; ++j) { + current[j] = prev[j-1] + prev[j]; // 左上角和右上角之和 + } + result.push_back(current); + } + return result; + } +}; +\end{Code} + + +\subsubsection{从右到左} +\begin{Code} +// LeetCode, Pascal's Triangle +// 时间复杂度O(n^2),空间复杂度O(n) +class Solution { +public: + vector > generate(int numRows) { + vector > result; + vector array; + for (int i = 1; i <= numRows; i++) { + for (int j = i - 2; j > 0; j--) { + array[j] = array[j - 1] + array[j]; + } + array.push_back(1); + result.push_back(array); + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Pascal's Triangle II,见 \S \ref{sec:pascals-triangle-ii} +\myenddot + + +\section{Pascal's Triangle II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:pascals-triangle-ii} + + +\subsubsection{描述} +Given an index $k$, return the $k^{th}$ row of the Pascal's triangle. + +For example, given $k = 3$, + +Return \code{[1,3,3,1]}. + +Note: Could you optimize your algorithm to use only $O(k)$ extra space? + + +\subsubsection{分析} +滚动数组。 + + +\subsubsection{代码} + +\begin{Code} +// LeetCode, Pascal's Triangle II +// 滚动数组,时间复杂度O(n^2),空间复杂度O(n) +class Solution { +public: + vector getRow(int rowIndex) { + vector array; + for (int i = 0; i <= rowIndex; i++) { + for (int j = i - 1; j > 0; j--){ + array[j] = array[j - 1] + array[j]; + } + array.push_back(1); + } + return array; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Pascal's Triangle,见 \S \ref{sec:pascals-triangle} +\myenddot + + +\section{Spiral Matrix} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:spiral-matrix} + + +\subsubsection{描述} +Given a matrix of $m \times n$ elements ($m$ rows, $n$ columns), return all elements of the matrix in spiral order. + +For example, +Given the following matrix: +\begin{Code} +[ + [ 1, 2, 3 ], + [ 4, 5, 6 ], + [ 7, 8, 9 ] +] +\end{Code} +You should return \fn{[1,2,3,6,9,8,7,4,5]}. + + +\subsubsection{分析} +模拟。 + +\subsubsection{代码} +\begin{Code} +// LeetCode, Spiral Matrix +// @author 龚陆安 (http://weibo.com/luangong) +// 时间复杂度O(n^2),空间复杂度O(1) +class Solution { +public: + vector spiralOrder(vector >& matrix) { + vector result; + if (matrix.empty()) return result; + int beginX = 0, endX = matrix[0].size() - 1; + int beginY = 0, endY = matrix.size() - 1; + while (true) { + // From left to right + for (int j = beginX; j <= endX; ++j) result.push_back(matrix[beginY][j]); + if (++beginY > endY) break; + // From top to bottom + for (int i = beginY; i <= endY; ++i) result.push_back(matrix[i][endX]); + if (beginX > --endX) break; + // From right to left + for (int j = endX; j >= beginX; --j) result.push_back(matrix[endY][j]); + if (beginY > --endY) break; + // From bottom to top + for (int i = endY; i >= beginY; --i) result.push_back(matrix[i][beginX]); + if (++beginX > endX) break; + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Spiral Matrix II ,见 \S \ref{sec:spiral-matrix-ii} +\myenddot + + +\section{Spiral Matrix II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:spiral-matrix-ii} + + +\subsubsection{描述} +Given an integer $n$, generate a square matrix filled with elements from 1 to $n^2$ in spiral order. + +For example, +Given $n = 3$, + +You should return the following matrix: +\begin{Code} +[ + [ 1, 2, 3 ], + [ 8, 9, 4 ], + [ 7, 6, 5 ] +] +\end{Code} + + +\subsubsection{分析} +这题比上一题要简单。 + + +\subsubsection{代码1} +\begin{Code} +// LeetCode, Spiral Matrix II +// 时间复杂度O(n^2),空间复杂度O(n^2) +class Solution { +public: + vector > generateMatrix(int n) { + vector > matrix(n, vector(n)); + int begin = 0, end = n - 1; + int num = 1; + + while (begin < end) { + for (int j = begin; j < end; ++j) matrix[begin][j] = num++; + for (int i = begin; i < end; ++i) matrix[i][end] = num++; + for (int j = end; j > begin; --j) matrix[end][j] = num++; + for (int i = end; i > begin; --i) matrix[i][begin] = num++; + ++begin; + --end; + } + + if (begin == end) matrix[begin][begin] = num; + + return matrix; + } +}; +\end{Code} + + +\subsubsection{代码2} +\begin{Code} +// LeetCode, Spiral Matrix II +// @author 龚陆安 (http://weibo.com/luangong) +// 时间复杂度O(n^2),空间复杂度O(n^2) +class Solution { +public: + vector > generateMatrix(int n) { + vector< vector > matrix(n, vector(n)); + if (n == 0) return matrix; + int beginX = 0, endX = n - 1; + int beginY = 0, endY = n - 1; + int num = 1; + while (true) { + for (int j = beginX; j <= endX; ++j) matrix[beginY][j] = num++; + if (++beginY > endY) break; + + for (int i = beginY; i <= endY; ++i) matrix[i][endX] = num++; + if (beginX > --endX) break; + + for (int j = endX; j >= beginX; --j) matrix[endY][j] = num++; + if (beginY > --endY) break; + + for (int i = endY; i >= beginY; --i) matrix[i][beginX] = num++; + if (++beginX > endX) break; + } + return matrix; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Spiral Matrix, 见 \S \ref{sec:spiral-matrix} +\myenddot + + +\section{ZigZag Conversion} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:zigzag-conversion} + + +\subsubsection{描述} +The string \code{"PAYPALISHIRING"} is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility) + +\begin{Code} +P A H N +A P L S I I G +Y I R +\end{Code} + +And then read line by line: \code{"PAHNAPLSIIGYIR"} + +Write the code that will take a string and make this conversion given a number of rows: +\begin{Code} +string convert(string text, int nRows); +\end{Code} +\code{convert("PAYPALISHIRING", 3)} should return \code{"PAHNAPLSIIGYIR"}. + + +\subsubsection{分析} +要找到数学规律。真正面试中,不大可能出这种问题。 + +n=4: +\begin{Code} +P I N +A L S I G +Y A H R +P I +\end{Code} + +n=5: +\begin{Code} +P H +A S I +Y I R +P L I G +A N +\end{Code} + +所以,对于每一层垂直元素的坐标 $(i,j)= (j+1 )*n +i$;对于每两层垂直元素之间的插入元素(斜对角元素),$(i,j)= (j+1)*n -i$ + + +\subsubsection{代码} +\begin{Code} +// LeetCode, ZigZag Conversion +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + string convert(string s, int nRows) { + if (nRows <= 1 || s.size() <= 1) return s; + string result; + for (int i = 0; i < nRows; i++) { + for (int j = 0, index = i; index < s.size(); + j++, index = (2 * nRows - 2) * j + i) { + result.append(1, s[index]); // 垂直元素 + if (i == 0 || i == nRows - 1) continue; // 斜对角元素 + if (index + (nRows - i - 1) * 2 < s.size()) + result.append(1, s[index + (nRows - i - 1) * 2]); + } + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{Divide Two Integers} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:divide-two-integers} + + +\subsubsection{描述} +Divide two integers without using multiplication, division and mod operator. + + +\subsubsection{分析} +不能用乘、除和取模,那剩下的,还有加、减和位运算。 + +最简单的方法,是不断减去被除数。在这个基础上,可以做一点优化,每次把被除数翻倍,从而加速。 + + +\subsubsection{代码1} +\begin{Code} +// LeetCode, Divide Two Integers +// 时间复杂度O(logn),空间复杂度O(1) +class Solution { +public: + int divide(int dividend, int divisor) { + // 当 dividend = INT_MIN时,-dividend会溢出,所以用 long long + long long a = dividend >= 0 ? dividend : -(long long)dividend; + long long b = divisor >= 0 ? divisor : -(long long)divisor; + + // 当 dividend = INT_MIN时,divisor = -1时,结果会溢出,所以用 long long + long long result = 0; + while (a >= b) { + long long c = b; + for (int i = 0; a >= c; ++i, c <<= 1) { + a -= c; + result += 1 << i; + } + } + + return ((dividend^divisor) >> 31) ? (-result) : (result); + } +}; +\end{Code} + + +\subsubsection{代码2} +\begin{Code} +// LeetCode, Divide Two Integers +// 时间复杂度O(logn),空间复杂度O(1) +class Solution { +public: + int divide(int dividend, int divisor) { + int result = 0; // 当 dividend = INT_MIN时,divisor = -1时,结果会溢出 + const bool sign = (dividend > 0 && divisor < 0) || + (dividend < 0 && divisor > 0); // 异号 + + // 当 dividend = INT_MIN时,-dividend会溢出,所以用 unsigned int + unsigned int a = dividend >= 0 ? dividend : -dividend; + unsigned int b = divisor >= 0 ? divisor : -divisor; + + while (a >= b) { + int multi = 1; + unsigned int bb = b; + while (a >= bb) { + a -= bb; + result += multi; + + if (bb < INT_MAX >> 1) { // 防止溢出 + bb += bb; + multi += multi; + } + } + } + if (sign) return -result; + else return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{Text Justification} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:text-justification} + + +\subsubsection{描述} +Given an array of words and a length $L$, format the text such that each line has exactly $L$ characters and is fully (left and right) justified. + +You should pack your words in a greedy approach; that is, pack as many words as you can in each line. Pad extra spaces \fn{' '} when necessary so that each line has exactly $L$ characters. + +Extra spaces between words should be distributed as evenly as possible. If the number of spaces on a line do not divide evenly between words, the empty slots on the left will be assigned more spaces than the slots on the right. + +For the last line of text, it should be left justified and no extra space is inserted between words. + +For example, \\ +words: \code{["This", "is", "an", "example", "of", "text", "justification."]} \\ +L: 16. + +Return the formatted lines as: +\begin{Code} +[ + "This is an", + "example of text", + "justification. " +] +\end{Code} + +Note: Each word is guaranteed not to exceed $L$ in length. + +Corner Cases: +\begindot +\item A line other than the last line might contain only one word. What should you do in this case? +\item In this case, that line should be left +\myenddot + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Text Justification +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + vector fullJustify(vector &words, int L) { + vector result; + const int n = words.size(); + int begin = 0, len = 0; // 当前行的起点,当前长度 + for (int i = 0; i < n; ++i) { + if (len + words[i].size() + (i - begin) > L) { + result.push_back(connect(words, begin, i - 1, len, L, false)); + begin = i; + len = 0; + } + len += words[i].size(); + } + // 最后一行不足L + result.push_back(connect(words, begin, n - 1, len, L, true)); + return result; + } + /** + * @brief 将 words[begin, end] 连成一行 + * @param[in] words 单词列表 + * @param[in] begin 开始 + * @param[in] end 结束 + * @param[in] len words[begin, end]所有单词加起来的长度 + * @param[in] L 题目规定的一行长度 + * @param[in] is_last 是否是最后一行 + * @return 对齐后的当前行 + */ + string connect(vector &words, int begin, int end, + int len, int L, bool is_last) { + string s; + int n = end - begin + 1; + for (int i = 0; i < n; ++i) { + s += words[begin + i]; + addSpaces(s, i, n - 1, L - len, is_last); + } + + if (s.size() < L) s.append(L - s.size(), ' '); + return s; + } + + /** + * @brief 添加空格. + * @param[inout]s 一行 + * @param[in] i 当前空隙的序号 + * @param[in] n 空隙总数 + * @param[in] L 总共需要添加的空额数 + * @param[in] is_last 是否是最后一行 + * @return 无 + */ + void addSpaces(string &s, int i, int n, int L, bool is_last) { + if (n < 1 || i > n - 1) return; + int spaces = is_last ? 1 : (L / n + (i < (L % n) ? 1 : 0)); + s.append(spaces, ' '); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{Max Points on a Line} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:Max-Points-on-a-Line} + + +\subsubsection{描述} +Given $n$ points on a 2D plane, find the maximum number of points that lie on the same straight line. + + +\subsubsection{分析} +暴力枚举法。两点决定一条直线,$n$个点两两组合,可以得到$\dfrac{1}{2}n(n+1)$条直线,对每一条直线,判断$n$个点是否在该直线上,从而可以得到这条直线上的点的个数,选择最大的那条直线返回。复杂度$O(n^3)$。 + +上面的暴力枚举法以“边”为中心,再看另一种暴力枚举法,以每个“点”为中心,然后遍历剩余点,找到所有的斜率,如果斜率相同,那么一定共线对每个点,用一个哈希表,key为斜率,value为该直线上的点数,计算出哈希表后,取最大值,并更新全局最大值,最后就是结果。时间复杂度$O(n^2)$,空间复杂度$O(n)$。 + + +\subsubsection{以边为中心} +\begin{Code} +// LeetCode, Max Points on a Line +// 暴力枚举法,以边为中心,时间复杂度O(n^3),空间复杂度O(1) +class Solution { +public: + int maxPoints(vector &points) { + if (points.size() < 3) return points.size(); + int result = 0; + + for (int i = 0; i < points.size() - 1; i++) { + for (int j = i + 1; j < points.size(); j++) { + int sign = 0; + int a, b, c; + if (points[i].x == points[j].x) sign = 1; + else { + a = points[j].x - points[i].x; + b = points[j].y - points[i].y; + c = a * points[i].y - b * points[i].x; + } + int count = 0; + for (int k = 0; k < points.size(); k++) { + if ((0 == sign && a * points[k].y == c + b * points[k].x) || + (1 == sign&&points[k].x == points[j].x)) + count++; + } + if (count > result) result = count; + } + } + return result; + } +}; +\end{Code} + + +\subsubsection{以点为中心} +\begin{Code} +// LeetCode, Max Points on a Line +// 暴力枚举,以点为中心,时间复杂度O(n^2),空间复杂度O(n) +class Solution { +public: + int maxPoints(vector &points) { + if (points.size() < 3) return points.size(); + int result = 0; + + unordered_map slope_count; + for (int i = 0; i < points.size()-1; i++) { + slope_count.clear(); + int samePointNum = 0; // 与i重合的点 + int point_max = 1; // 和i共线的最大点数 + + for (int j = i + 1; j < points.size(); j++) { + double slope; // 斜率 + if (points[i].x == points[j].x) { + slope = std::numeric_limits::infinity(); + if (points[i].y == points[j].y) { + ++ samePointNum; + continue; + } + } else { + slope = 1.0 * (points[i].y - points[j].y) / + (points[i].x - points[j].x); + } + + int count = 0; + if (slope_count.find(slope) != slope_count.end()) + count = ++slope_count[slope]; + else { + count = 2; + slope_count[slope] = 2; + } + + if (point_max < count) point_max = count; + } + result = max(result, point_max + samePointNum); + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot diff --git a/leetcode/C++/chapLinearList.tex b/leetcode/C++/chapLinearList.tex new file mode 100644 index 00000000..c23c4538 --- /dev/null +++ b/leetcode/C++/chapLinearList.tex @@ -0,0 +1,2915 @@ +\chapter{线性表} +这类题目考察线性表的操作,例如,数组,单链表,双向链表等。 + + +\section{数组} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +\subsection{Remove Duplicates from Sorted Array} +\label{sec:remove-duplicates-from-sorted-array} + + +\subsubsection{描述} +Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length. + +Do not allocate extra space for another array, you must do this in place with constant memory. + +For example, Given input array \code{A = [1,1,2]}, + +Your function should return length = 2, and A is now \code{[1,2]}. + + +\subsubsection{分析} +无 + + +\subsubsection{代码1} +\begin{Code} +// LeetCode, Remove Duplicates from Sorted Array +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int removeDuplicates(int A[], int n) { + if (n == 0) return 0; + + int index = 0; + for (int i = 1; i < n; i++) { + if (A[index] != A[i]) + A[++index] = A[i]; + } + return index + 1; + } +}; +\end{Code} + + +\subsubsection{代码2} +\begin{Code} +// LeetCode, Remove Duplicates from Sorted Array +// 使用STL,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int removeDuplicates(int A[], int n) { + return distance(A, unique(A, A + n)); + } +}; +\end{Code} + + +\subsubsection{代码3} +\begin{Code} +// LeetCode, Remove Duplicates from Sorted Array +// 使用STL,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int removeDuplicates(int A[], int n) { + return removeDuplicates(A, A + n, A) - A; + } + + template + OutIt removeDuplicates(InIt first, InIt last, OutIt output) { + while (first != last) { + *output++ = *first; + first = upper_bound(first, last, *first); + } + + return output; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item Remove Duplicates from Sorted Array II,见 \S \ref{sec:remove-duplicates-from-sorted-array-ii} +\myenddot + + +\subsection{Remove Duplicates from Sorted Array II} +\label{sec:remove-duplicates-from-sorted-array-ii} + + +\subsubsection{描述} +Follow up for "Remove Duplicates": +What if duplicates are allowed at most twice? + +For example, +Given sorted array \code{A = [1,1,1,2,2,3]}, + +Your function should return length = 5, and A is now \code{[1,1,2,2,3]} + + +\subsubsection{分析} +加一个变量记录一下元素出现的次数即可。这题因为是已经排序的数组,所以一个变量即可解决。如果是没有排序的数组,则需要引入一个hashmap来记录出现次数。 + + +\subsubsection{代码1} +\begin{Code} +// LeetCode, Remove Duplicates from Sorted Array II +// 时间复杂度O(n),空间复杂度O(1) +// @author hex108 (https://github.com/hex108) +class Solution { +public: + int removeDuplicates(int A[], int n) { + if (n <= 2) return n; + + int index = 2; + for (int i = 2; i < n; i++){ + if (A[i] != A[index - 2]) + A[index++] = A[i]; + } + + return index; + } +}; +\end{Code} + + +\subsubsection{代码2} +下面是一个更简洁的版本。上面的代码略长,不过扩展性好一些,例如将\fn{occur < 2}改为\fn{occur < 3},就变成了允许重复最多3次。 +\begin{Code} +// LeetCode, Remove Duplicates from Sorted Array II +// @author 虞航仲 (http://weibo.com/u/1666779725) +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int removeDuplicates(int A[], int n) { + int index = 0; + for (int i = 0; i < n; ++i) { + if (i > 0 && i < n - 1 && A[i] == A[i - 1] && A[i] == A[i + 1]) + continue; + + A[index++] = A[i]; + } + return index; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item Remove Duplicates from Sorted Array,见 \S \ref{sec:remove-duplicates-from-sorted-array} +\myenddot + + +\subsection{Search in Rotated Sorted Array} +\label{sec:search-in-rotated-sorted-array} + + +\subsubsection{描述} +Suppose a sorted array is rotated at some pivot unknown to you beforehand. + +(i.e., \code{0 1 2 4 5 6 7} might become \code{4 5 6 7 0 1 2}). + +You are given a target value to search. If found in the array return its index, otherwise return -1. + +You may assume no duplicate exists in the array. + + +\subsubsection{分析} +二分查找,难度主要在于左右边界的确定。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Search in Rotated Sorted Array +// 时间复杂度O(log n),空间复杂度O(1) +class Solution { +public: + int search(int A[], int n, int target) { + int first = 0, last = n; + while (first != last) { + const int mid = first + (last - first) / 2; + if (A[mid] == target) + return mid; + if (A[first] <= A[mid]) { + if (A[first] <= target && target < A[mid]) + last = mid; + else + first = mid + 1; + } else { + if (A[mid] < target && target <= A[last-1]) + first = mid + 1; + else + last = mid; + } + } + return -1; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item Search in Rotated Sorted Array II,见 \S \ref{sec:search-in-rotated-sorted-array-ii} +\myenddot + + +\subsection{Search in Rotated Sorted Array II} +\label{sec:search-in-rotated-sorted-array-ii} + + +\subsubsection{描述} +Follow up for "Search in Rotated Sorted Array": What if \emph{duplicates} are allowed? + +Would this affect the run-time complexity? How and why? + +Write a function to determine if a given target is in the array. + + +\subsubsection{分析} +允许重复元素,则上一题中如果\fn{A[m]>=A[l]},那么\fn{[l,m]}为递增序列的假设就不能成立了,比如\code{[1,3,1,1,1]}。 + +如果\fn{A[m]>=A[l]}不能确定递增,那就把它拆分成两个条件: +\begindot +\item 若\fn{A[m]>A[l]},则区间\fn{[l,m]}一定递增 +\item 若\fn{A[m]==A[l]} 确定不了,那就\fn{l++},往下看一步即可。 +\myenddot + +\subsubsection{代码} +\begin{Code} +// LeetCode, Search in Rotated Sorted Array II +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + bool search(int A[], int n, int target) { + int first = 0, last = n; + while (first != last) { + const int mid = first + (last - first) / 2; + if (A[mid] == target) + return true; + if (A[first] < A[mid]) { + if (A[first] <= target && target < A[mid]) + last = mid; + else + first = mid + 1; + } else if (A[first] > A[mid]) { + if (A[mid] < target && target <= A[last-1]) + first = mid + 1; + else + last = mid; + } else + //skip duplicate one + first++; + } + return false; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item Search in Rotated Sorted Array,见 \S \ref{sec:search-in-rotated-sorted-array} +\myenddot + + +\subsection{Median of Two Sorted Arrays} +\label{sec:median-of-two-sorted-arrays} + + +\subsubsection{描述} +There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be $O(\log (m+n))$. + + +\subsubsection{分析} +这是一道非常经典的题。这题更通用的形式是,给定两个已经排序好的数组,找到两者所有元素中第$k$大的元素。 + +$O(m+n)$的解法比较直观,直接merge两个数组,然后求第$k$大的元素。 + +不过我们仅仅需要第$k$大的元素,是不需要“排序”这么复杂的操作的。可以用一个计数器,记录当前已经找到第$m$大的元素了。同时我们使用两个指针\fn{pA}和\fn{pB},分别指向A和B数组的第一个元素,使用类似于merge sort的原理,如果数组A当前元素小,那么\fn{pA++},同时\fn{m++};如果数组B当前元素小,那么\fn{pB++},同时\fn{m++}。最终当$m$等于$k$的时候,就得到了我们的答案,$O(k)$时间,$O(1)$空间。但是,当$k$很接近$m+n$的时候,这个方法还是$O(m+n)$的。 + +有没有更好的方案呢?我们可以考虑从$k$入手。如果我们每次都能够删除一个一定在第$k$大元素之前的元素,那么我们需要进行$k$次。但是如果每次我们都删除一半呢?由于A和B都是有序的,我们应该充分利用这里面的信息,类似于二分查找,也是充分利用了“有序”。 + +假设A和B的元素个数都大于$k/2$,我们将A的第$k/2$个元素(即\fn{A[k/2-1]})和B的第$k/2$个元素(即\fn{B[k/2-1]})进行比较,有以下三种情况(为了简化这里先假设$k$为偶数,所得到的结论对于$k$是奇数也是成立的): +\begindot +\item \fn{A[k/2-1] == B[k/2-1]} +\item \fn{A[k/2-1] > B[k/2-1]} +\item \fn{A[k/2-1] < B[k/2-1]} +\myenddot + +如果\fn{A[k/2-1] < B[k/2-1]},意味着\fn{A[0]}到\fn{A[k/2-1}的肯定在$A \cup B$的top k元素的范围内,换句话说,\fn{A[k/2-1}不可能大于$A \cup B$的第$k$大元素。留给读者证明。 + +因此,我们可以放心的删除A数组的这$k/2$个元素。同理,当\fn{A[k/2-1] > B[k/2-1]}时,可以删除B数组的$k/2$个元素。 + +当\fn{A[k/2-1] == B[k/2-1]}时,说明找到了第$k$大的元素,直接返回\fn{A[k/2-1]}或\fn{B[k/2-1]}即可。 + +因此,我们可以写一个递归函数。那么函数什么时候应该终止呢? +\begindot +\item 当A或B是空时,直接返回\fn{B[k-1]}或\fn{A[k-1]}; +\item 当\fn{k=1}是,返回\fn{min(A[0], B[0])}; +\item 当\fn{A[k/2-1] == B[k/2-1]}时,返回\fn{A[k/2-1]}或\fn{B[k/2-1]} +\myenddot + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Median of Two Sorted Arrays +// 时间复杂度O(log(m+n)),空间复杂度O(log(m+n)) +class Solution { +public: + double findMedianSortedArrays(int A[], int m, int B[], int n) { + int total = m + n; + if (total & 0x1) + return find_kth(A, m, B, n, total / 2 + 1); + else + return (find_kth(A, m, B, n, total / 2) + + find_kth(A, m, B, n, total / 2 + 1)) / 2.0; + } +private: + static int find_kth(int A[], int m, int B[], int n, int k) { + //always assume that m is equal or smaller than n + if (m > n) return find_kth(B, n, A, m, k); + if (m == 0) return B[k - 1]; + if (k == 1) return min(A[0], B[0]); + + //divide k into two parts + int ia = min(k / 2, m), ib = k - ia; + if (A[ia - 1] < B[ib - 1]) + return find_kth(A + ia, m - ia, B, n, k - ia); + else if (A[ia - 1] > B[ib - 1]) + return find_kth(A, m, B + ib, n - ib, k - ib); + else + return A[ia - 1]; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item 无 +\myenddot + + +\subsection{Longest Consecutive Sequence} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:longest-consecutive-sequence} + + +\subsubsection{描述} +Given an unsorted array of integers, find the length of the longest consecutive elements sequence. + +For example, +Given \code{[100, 4, 200, 1, 3, 2]}, +The longest consecutive elements sequence is \code{[1, 2, 3, 4]}. Return its length: 4. + +Your algorithm should run in $O(n)$ complexity. + + +\subsubsection{分析} +如果允许$O(n \log n)$的复杂度,那么可以先排序,可是本题要求$O(n)$。 + +由于序列里的元素是无序的,又要求$O(n)$,首先要想到用哈希表。 + +用一个哈希表 \fn{unordered_map used}记录每个元素是否使用,对每个元素,以该元素为中心,往左右扩张,直到不连续为止,记录下最长的长度。 + + +\subsubsection{代码} +\begin{Code} +// Leet Code, Longest Consecutive Sequence +// 时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + int longestConsecutive(const vector &num) { + unordered_map used; + + for (auto i : num) used[i] = false; + + int longest = 0; + + for (auto i : num) { + if (used[i]) continue; + + int length = 1; + + used[i] = true; + + for (int j = i + 1; used.find(j) != used.end(); ++j) { + used[j] = true; + ++length; + } + + for (int j = i - 1; used.find(j) != used.end(); --j) { + used[j] = true; + ++length; + } + + longest = max(longest, length); + } + + return longest; + } +}; +\end{Code} + +\subsubsection{分析2} +第一直觉是个聚类的操作,应该有union,find的操作.连续序列可以用两端和长度来表示. +本来用两端就可以表示,但考虑到查询的需求,将两端分别暴露出来.用\fn{unordered_map map}来 +存储.原始思路来自于\url{http://discuss.leetcode.com/questions/1070/longest-consecutive-sequence} + +\subsubsection{代码} + +\begin{Code} +// Leet Code, Longest Consecutive Sequence +// 时间复杂度O(n),空间复杂度O(n) +// Author: @advancedxy +class Solution { +public: + int longestConsecutive(vector &num) { + unordered_map map; + int size = num.size(); + int l = 1; + for (int i = 0; i < size; i++) { + if (map.find(num[i]) != map.end()) continue; + map[num[i]] = 1; + if (map.find(num[i] - 1) != map.end()) { + l = max(l, mergeCluster(map, num[i] - 1, num[i])); + } + if (map.find(num[i] + 1) != map.end()) { + l = max(l, mergeCluster(map, num[i], num[i] + 1)); + } + } + return size == 0 ? 0 : l; + } + +private: + int mergeCluster(unordered_map &map, int left, int right) { + int upper = right + map[right] - 1; + int lower = left - map[left] + 1; + int length = upper - lower + 1; + map[upper] = length; + map[lower] = length; + return length; + } +}; +\end{Code} + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\subsection{Two Sum} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:Two-sum} + + +\subsubsection{描述} +Given an array of integers, find two numbers such that they add up to a specific target number. + +The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based. + +You may assume that each input would have exactly one solution. + +Input: \code{numbers=\{2, 7, 11, 15\}, target=9} + +Output: \code{index1=1, index2=2} + + +\subsubsection{分析} +方法1:暴力,复杂度$O(n^2)$,会超时 + +方法2:hash。用一个哈希表,存储每个数对应的下标,复杂度$O(n)$. + +方法3:先排序,然后左右夹逼,排序$O(n\log n)$,左右夹逼$O(n)$,最终$O(n\log n)$。但是注意,这题需要返回的是下标,而不是数字本身,因此这个方法行不通。 + + +\subsubsection{代码} +\begin{Code} +//LeetCode, Two Sum +// 方法2:hash。用一个哈希表,存储每个数对应的下标 +// 时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + vector twoSum(vector &num, int target) { + unordered_map mapping; + vector result; + for (int i = 0; i < num.size(); i++) { + mapping[num[i]] = i; + } + for (int i = 0; i < num.size(); i++) { + const int gap = target - num[i]; + if (mapping.find(gap) != mapping.end() && mapping[gap] > i) { + result.push_back(i + 1); + result.push_back(mapping[gap] + 1); + break; + } + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 3Sum, 见 \S \ref{sec:3sum} +\item 3Sum Closest, 见 \S \ref{sec:3sum-closest} +\item 4Sum, 见 \S \ref{sec:4sum} +\myenddot + + +\subsection{3Sum} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:3sum} + + +\subsubsection{描述} +Given an array $S$ of $n$ integers, are there elements $a, b, c$ in $S$ such that $a + b + c = 0$? Find all unique triplets in the array which gives the sum of zero. + +Note: +\begindot +\item Elements in a triplet $(a,b,c)$ must be in non-descending order. (ie, $a \leq b \leq c$) +\item The solution set must not contain duplicate triplets. +\myenddot + +For example, given array \code{S = \{-1 0 1 2 -1 -4\}}. + +A solution set is: +\begin{Code} +(-1, 0, 1) +(-1, -1, 2) +\end{Code} + + +\subsubsection{分析} +先排序,然后左右夹逼,复杂度 $O(n^2)$。 + +这个方法可以推广到$k$-sum,先排序,然后做$k-2$次循环,在最内层循环左右夹逼,时间复杂度是 $O(\max\{n \log n, n^{k-1}\})$。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, 3Sum +// 先排序,然后左右夹逼,注意跳过重复的数,时间复杂度O(n^2),空间复杂度O(1) +class Solution { + public: + vector> threeSum(vector& num) { + vector> result; + if (num.size() < 3) return result; + sort(num.begin(), num.end()); + const int target = 0; + + auto last = num.end(); + for (auto i = num.begin(); i < last-2; ++i) { + auto j = i+1; + if (i > num.begin() && *i == *(i-1)) continue; + auto k = last-1; + while (j < k) { + if (*i + *j + *k < target) { + ++j; + while(*j == *(j - 1) && j < k) ++j; + } else if (*i + *j + *k > target) { + --k; + while(*k == *(k + 1) && j < k) --k; + } else { + result.push_back({ *i, *j, *k }); + ++j; + --k; + while(*j == *(j - 1) && *k == *(k + 1) && j < k) ++j; + } + } + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Two sum, 见 \S \ref{sec:Two-sum} +\item 3Sum Closest, 见 \S \ref{sec:3sum-closest} +\item 4Sum, 见 \S \ref{sec:4sum} +\myenddot + +\subsection{3Sum Closest} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:3sum-closest} + + +\subsubsection{描述} +Given an array $S$ of $n$ integers, find three integers in $S$ such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution. + +For example, given array \code{S = \{-1 2 1 -4\}}, and \code{target = 1}. + +The sum that is closest to the target is 2. (\code{-1 + 2 + 1 = 2}). + + +\subsubsection{分析} +先排序,然后左右夹逼,复杂度 $O(n^2)$。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, 3Sum Closest +// 先排序,然后左右夹逼,时间复杂度O(n^2),空间复杂度O(1) +class Solution { +public: + int threeSumClosest(vector& num, int target) { + int result = 0; + int min_gap = INT_MAX; + + sort(num.begin(), num.end()); + + for (auto a = num.begin(); a != prev(num.end(), 2); ++a) { + auto b = next(a); + auto c = prev(num.end()); + + while (b < c) { + const int sum = *a + *b + *c; + const int gap = abs(sum - target); + + if (gap < min_gap) { + result = sum; + min_gap = gap; + } + + if (sum < target) ++b; + else --c; + } + } + + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Two sum, 见 \S \ref{sec:Two-sum} +\item 3Sum, 见 \S \ref{sec:3sum} +\item 4Sum, 见 \S \ref{sec:4sum} +\myenddot + + +\subsection{4Sum} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:4sum} + + +\subsubsection{描述} +Given an array $S$ of $n$ integers, are there elements $a, b, c$, and $d$ in $S$ such that $a + b + c + d = target$? Find all unique quadruplets in the array which gives the sum of target. + +Note: +\begindot +\item Elements in a quadruplet $(a,b,c,d)$ must be in non-descending order. (ie, $a \leq b \leq c \leq d$) +\item The solution set must not contain duplicate quadruplets. +\myenddot + +For example, given array \code{S = \{1 0 -1 0 -2 2\}}, and \code{target = 0}. + +A solution set is: +\begin{Code} +(-1, 0, 0, 1) +(-2, -1, 1, 2) +(-2, 0, 0, 2) +\end{Code} + + +\subsubsection{分析} +先排序,然后左右夹逼,复杂度 $O(n^3)$,会超时。 + +可以用一个hashmap先缓存两个数的和,最终复杂度$O(n^3)$。这个策略也适用于 3Sum 。 + + +\subsubsection{左右夹逼} +\begin{Code} +// LeetCode, 4Sum +// 先排序,然后左右夹逼,时间复杂度O(n^3),空间复杂度O(1) +class Solution { +public: + vector> fourSum(vector& num, int target) { + vector> result; + if (num.size() < 4) return result; + sort(num.begin(), num.end()); + + auto last = num.end(); + for (auto a = num.begin(); a < prev(last, 3); ++a) { + for (auto b = next(a); b < prev(last, 2); ++b) { + auto c = next(b); + auto d = prev(last); + while (c < d) { + if (*a + *b + *c + *d < target) { + ++c; + } else if (*a + *b + *c + *d > target) { + --d; + } else { + result.push_back({ *a, *b, *c, *d }); + ++c; + --d; + } + } + } + } + sort(result.begin(), result.end()); + result.erase(unique(result.begin(), result.end()), result.end()); + return result; + } +}; +\end{Code} + + +\subsubsection{map做缓存} +\begin{Code} +// LeetCode, 4Sum +// 用一个hashmap先缓存两个数的和 +// 时间复杂度,平均O(n^2),最坏O(n^4),空间复杂度O(n^2) +class Solution { +public: + vector > fourSum(vector &num, int target) { + vector> result; + if (num.size() < 4) return result; + sort(num.begin(), num.end()); + + unordered_map > > cache; + for (size_t a = 0; a < num.size(); ++a) { + for (size_t b = a + 1; b < num.size(); ++b) { + cache[num[a] + num[b]].push_back(pair(a, b)); + } + } + + for (int c = 0; c < num.size(); ++c) { + for (size_t d = c + 1; d < num.size(); ++d) { + const int key = target - num[c] - num[d]; + if (cache.find(key) == cache.end()) continue; + + const auto& vec = cache[key]; + for (size_t k = 0; k < vec.size(); ++k) { + if (c <= vec[k].second) + continue; // 有重叠 + + result.push_back( { num[vec[k].first], + num[vec[k].second], num[c], num[d] }); + } + } + } + sort(result.begin(), result.end()); + result.erase(unique(result.begin(), result.end()), result.end()); + return result; + } +}; +\end{Code} + + +\subsubsection{multimap} +\begin{Code} +// LeetCode, 4Sum +// 用一个 hashmap 先缓存两个数的和 +// 时间复杂度O(n^2),空间复杂度O(n^2) +// @author 龚陆安(http://weibo.com/luangong) +class Solution { +public: + vector> fourSum(vector& num, int target) { + vector> result; + if (num.size() < 4) return result; + sort(num.begin(), num.end()); + + unordered_multimap> cache; + for (int i = 0; i + 1 < num.size(); ++i) + for (int j = i + 1; j < num.size(); ++j) + cache.insert(make_pair(num[i] + num[j], make_pair(i, j))); + + for (auto i = cache.begin(); i != cache.end(); ++i) { + int x = target - i->first; + auto range = cache.equal_range(x); + for (auto j = range.first; j != range.second; ++j) { + auto a = i->second.first; + auto b = i->second.second; + auto c = j->second.first; + auto d = j->second.second; + if (a != c && a != d && b != c && b != d) { + vector vec = { num[a], num[b], num[c], num[d] }; + sort(vec.begin(), vec.end()); + result.push_back(vec); + } + } + } + sort(result.begin(), result.end()); + result.erase(unique(result.begin(), result.end()), result.end()); + return result; + } +}; +\end{Code} + + +\subsubsection{方法4} +\begin{Code} +// LeetCode, 4Sum +// 先排序,然后左右夹逼,时间复杂度O(n^3logn),空间复杂度O(1),会超时 +// 跟方法1相比,表面上优化了,实际上更慢了,切记! +class Solution { +public: + vector> fourSum(vector& num, int target) { + vector> result; + if (num.size() < 4) return result; + sort(num.begin(), num.end()); + + auto last = num.end(); + for (auto a = num.begin(); a < prev(last, 3); + a = upper_bound(a, prev(last, 3), *a)) { + for (auto b = next(a); b < prev(last, 2); + b = upper_bound(b, prev(last, 2), *b)) { + auto c = next(b); + auto d = prev(last); + while (c < d) { + if (*a + *b + *c + *d < target) { + c = upper_bound(c, d, *c); + } else if (*a + *b + *c + *d > target) { + d = prev(lower_bound(c, d, *d)); + } else { + result.push_back({ *a, *b, *c, *d }); + c = upper_bound(c, d, *c); + d = prev(lower_bound(c, d, *d)); + } + } + } + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Two sum, 见 \S \ref{sec:Two-sum} +\item 3Sum, 见 \S \ref{sec:3sum} +\item 3Sum Closest, 见 \S \ref{sec:3sum-closest} +\myenddot + + +\subsection{Remove Element} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:remove-element } + + +\subsubsection{描述} +Given an array and a value, remove all instances of that value in place and return the new length. + +The order of elements can be changed. It doesn't matter what you leave beyond the new length. + + +\subsubsection{分析} +无 + + +\subsubsection{代码1} +\begin{Code} +// LeetCode, Remove Element +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int removeElement(int A[], int n, int elem) { + int index = 0; + for (int i = 0; i < n; ++i) { + if (A[i] != elem) { + A[index++] = A[i]; + } + } + return index; + } +}; +\end{Code} + + +\subsubsection{代码2} +\begin{Code} +// LeetCode, Remove Element +// 使用remove(),时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int removeElement(int A[], int n, int elem) { + return distance(A, remove(A, A+n, elem)); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\subsection{Next Permutation} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:next-permutation} + + +\subsubsection{描述} +Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers. + +If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order). + +The replacement must be in-place, do not allocate extra memory. + +Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column. +\begin{Code} +1,2,3 → 1,3,2 +3,2,1 → 1,2,3 +1,1,5 → 1,5,1 +\end{Code} + + +\subsubsection{分析} +算法过程如图~\ref{fig:permutation}所示(来自\myurl{http://fisherlei.blogspot.com/2012/12/leetcode-next-permutation.html})。 + +\begin{center} +\includegraphics[width=360pt]{next-permutation.png}\\ +\figcaption{下一个排列算法流程}\label{fig:permutation} +\end{center} + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Next Permutation +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + void nextPermutation(vector &num) { + next_permutation(num.begin(), num.end()); + } + + template + bool next_permutation(BidiIt first, BidiIt last) { + // Get a reversed range to simplify reversed traversal. + const auto rfirst = reverse_iterator(last); + const auto rlast = reverse_iterator(first); + + // Begin from the second last element to the first element. + auto pivot = next(rfirst); + + // Find `pivot`, which is the first element that is no less than its + // successor. `Prev` is used since `pivort` is a `reversed_iterator`. + while (pivot != rlast && *pivot >= *prev(pivot)) + ++pivot; + + // No such elemenet found, current sequence is already the largest + // permutation, then rearrange to the first permutation and return false. + if (pivot == rlast) { + reverse(rfirst, rlast); + return false; + } + + // Scan from right to left, find the first element that is greater than + // `pivot`. + auto change = find_if(rfirst, pivot, bind1st(less(), *pivot)); + + swap(*change, *pivot); + reverse(rfirst, pivot); + + return true; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Permutation Sequence, 见 \S \ref{sec:permutation-sequence} +\item Permutations, 见 \S \ref{sec:permutations} +\item Permutations II, 见 \S \ref{sec:permutations-ii} +\item Combinations, 见 \S \ref{sec:combinations} +\myenddot + + +\subsection{Permutation Sequence} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:permutation-sequence} + + +\subsubsection{描述} +The set \fn{[1,2,3,…,n]} contains a total of $n!$ unique permutations. + +By listing and labeling all of the permutations in order, +We get the following sequence (ie, for $n = 3$): +\begin{Code} +"123" +"132" +"213" +"231" +"312" +"321" +\end{Code} + +Given $n$ and $k$, return the kth permutation sequence. + +Note: Given $n$ will be between 1 and 9 inclusive. + + +\subsubsection{分析} +简单的,可以用暴力枚举法,调用 $k-1$ 次 \fn{next_permutation()}。 + +暴力枚举法把前 $k$个排列都求出来了,比较浪费,而我们只需要第$k$个排列。 + +利用康托编码的思路,假设有$n$个不重复的元素,第$k$个排列是$a_1, a_2, a_3, ..., a_n$,那么$a_1$是哪一个位置呢? + +我们把$a_1$去掉,那么剩下的排列为 +$a_2, a_3, ..., a_n$, 共计$n-1$个元素,$n-1$个元素共有$(n-1)!$个排列,于是就可以知道 $a_1 = k / (n-1)!$。 + +同理,$a_2, a_3, ..., a_n$的值推导如下: + +\begin{eqnarray} +k_2 &=& k\%(n-1)! \nonumber \\ +a_2 &=& k_2/(n-2)! \nonumber \\ +\quad & \cdots \nonumber \\ +k_{n-1} &=& k_{n-2}\%2! \nonumber \\ +a_{n-1} &=& k_{n-1}/1! \nonumber \\ +a_n &=& 0 \nonumber +\end{eqnarray} + + +\subsubsection{使用next_permutation()} +\begin{Code} +// LeetCode, Permutation Sequence +// 使用next_permutation(),TLE +class Solution { +public: + string getPermutation(int n, int k) { + string s(n, '0'); + for (int i = 0; i < n; ++i) + s[i] += i+1; + for (int i = 0; i < k-1; ++i) + next_permutation(s.begin(), s.end()); + return s; + } + + template + bool next_permutation(BidiIt first, BidiIt last) { + // 代码见上一题 Next Permutation + } +}; +\end{Code} + + +\subsubsection{康托编码} +\begin{Code} +// LeetCode, Permutation Sequence +// 康托编码,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + string getPermutation(int n, int k) { + string s(n, '0'); + string result; + for (int i = 0; i < n; ++i) + s[i] += i + 1; + + return kth_permutation(s, k); + } +private: + int factorial(int n) { + int result = 1; + for (int i = 1; i <= n; ++i) + result *= i; + return result; + } + + // seq 已排好序,是第一个排列 + template + Sequence kth_permutation(const Sequence &seq, int k) { + const int n = seq.size(); + Sequence S(seq); + Sequence result; + + int base = factorial(n - 1); + --k; // 康托编码从0开始 + + for (int i = n - 1; i > 0; k %= base, base /= i, --i) { + auto a = next(S.begin(), k / base); + result.push_back(*a); + S.erase(a); + } + + result.push_back(S[0]); // 最后一个 + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Next Permutation, 见 \S \ref{sec:next-permutation} +\item Permutations, 见 \S \ref{sec:permutations} +\item Permutations II, 见 \S \ref{sec:permutations-ii} +\item Combinations, 见 \S \ref{sec:combinations} +\myenddot + + +\subsection{Valid Sudoku} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:valid-sudoku} + + +\subsubsection{描述} +Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules \myurl{http://sudoku.com.au/TheRules.aspx} . + +The Sudoku board could be partially filled, where empty cells are filled with the character \fn{'.'}. + +\begin{center} +\includegraphics[width=150pt]{sudoku.png}\\ +\figcaption{A partially filled sudoku which is valid}\label{fig:sudoku} +\end{center} + +\subsubsection{分析} +细节实现题。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Valid Sudoku +// 时间复杂度O(n^2),空间复杂度O(1) +class Solution { +public: + bool isValidSudoku(const vector>& board) { + bool used[9]; + + for (int i = 0; i < 9; ++i) { + fill(used, used + 9, false); + + for (int j = 0; j < 9; ++j) // 检查行 + if (!check(board[i][j], used)) + return false; + + fill(used, used + 9, false); + + for (int j = 0; j < 9; ++j) // 检查列 + if (!check(board[j][i], used)) + return false; + } + + for (int r = 0; r < 3; ++r) // 检查 9 个子格子 + for (int c = 0; c < 3; ++c) { + fill(used, used + 9, false); + + for (int i = r * 3; i < r * 3 + 3; ++i) + for (int j = c * 3; j < c * 3 + 3; ++j) + if (!check(board[i][j], used)) + return false; + } + + return true; + } + + bool check(char ch, bool used[9]) { + if (ch == '.') return true; + + if (used[ch - '1']) return false; + + return used[ch - '1'] = true; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Sudoku Solver, 见 \S \ref{sec:sudoku-solver} +\myenddot + + +\subsection{Trapping Rain Water} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:trapping-rain-water} + + +\subsubsection{描述} +Given $n$ non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining. + +For example, +Given \code{[0,1,0,2,1,0,1,3,2,1,2,1]}, return 6. + +\begin{center} +\includegraphics{trapping-rain-water.png}\\ +\figcaption{Trapping Rain Water}\label{fig:trapping-rain-water} +\end{center} + + +\subsubsection{分析} +对于每个柱子,找到其左右两边最高的柱子,该柱子能容纳的面积就是\code{min(max_left, max_right) - height}。所以, +\begin{enumerate} +\item 从左往右扫描一遍,对于每个柱子,求取左边最大值; +\item 从右往左扫描一遍,对于每个柱子,求最大右值; +\item 再扫描一遍,把每个柱子的面积并累加。 +\end{enumerate} + +也可以, +\begin{enumerate} +\item 扫描一遍,找到最高的柱子,这个柱子将数组分为两半; +\item 处理左边一半; +\item 处理右边一半。 +\end{enumerate} + + +\subsubsection{代码1} +\begin{Code} +// LeetCode, Trapping Rain Water +// 思路1,时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + int trap(int A[], int n) { + int *max_left = new int[n](); + int *max_right = new int[n](); + + for (int i = 1; i < n; i++) { + max_left[i] = max(max_left[i - 1], A[i - 1]); + max_right[n - 1 - i] = max(max_right[n - i], A[n - i]); + + } + + int sum = 0; + for (int i = 0; i < n; i++) { + int height = min(max_left[i], max_right[i]); + if (height > A[i]) { + sum += height - A[i]; + } + } + + delete[] max_left; + delete[] max_right; + return sum; + } +}; +\end{Code} + + +\subsubsection{代码2} +\begin{Code} +// LeetCode, Trapping Rain Water +// 思路2,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int trap(int A[], int n) { + int max = 0; // 最高的柱子,将数组分为两半 + for (int i = 0; i < n; i++) + if (A[i] > A[max]) max = i; + + int water = 0; + for (int i = 0, peak = 0; i < max; i++) + if (A[i] > peak) peak = A[i]; + else water += peak - A[i]; + for (int i = n - 1, top = 0; i > max; i--) + if (A[i] > top) top = A[i]; + else water += top - A[i]; + return water; + } +}; +\end{Code} + + +\subsubsection{代码3} +第三种解法,用一个栈辅助,小于栈顶的元素压入,大于等于栈顶就把栈里所有小于或等于当前值的元素全部出栈处理掉。 +\begin{Code} +// LeetCode, Trapping Rain Water +// 用一个栈辅助,小于栈顶的元素压入,大于等于栈顶就把栈里所有小于或 +// 等于当前值的元素全部出栈处理掉,计算面积,最后把当前元素入栈 +// 时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + int trap(int a[], int n) { + stack> s; + int water = 0; + + for (int i = 0; i < n; ++i) { + int height = 0; + + while (!s.empty()) { // 将栈里比当前元素矮或等高的元素全部处理掉 + int bar = s.top().first; + int pos = s.top().second; + // bar, height, a[i] 三者夹成的凹陷 + water += (min(bar, a[i]) - height) * (i - pos - 1); + height = bar; + + if (a[i] < bar) // 碰到了比当前元素高的,跳出循环 + break; + else + s.pop(); // 弹出栈顶,因为该元素处理完了,不再需要了 + } + + s.push(make_pair(a[i], i)); + } + + return water; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Container With Most Water, 见 \S \ref{sec:container-with-most-water} +\item Largest Rectangle in Histogram, 见 \S \ref{sec:largest-rectangle-in-histogram} +\myenddot + + +\subsection{Rotate Image} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:rotate-image} + + +\subsubsection{描述} +You are given an $n \times n$ 2D matrix representing an image. + +Rotate the image by 90 degrees (clockwise). + +Follow up: +Could you do this in-place? + + +\subsubsection{分析} +首先想到,纯模拟,从外到内一圈一圈的转,但这个方法太慢。 + +如下图,首先沿着副对角线翻转一次,然后沿着水平中线翻转一次。 + +\begin{center} +\includegraphics[width=200pt]{rotate-image.png}\\ +\figcaption{Rotate Image}\label{fig:rotate-image} +\end{center} + +或者,首先沿着水平中线翻转一次,然后沿着主对角线翻转一次。 + + +\subsubsection{代码1} +\begin{Code} +// LeetCode, Rotate Image +// 思路 1,时间复杂度O(n^2),空间复杂度O(1) +class Solution { +public: + void rotate(vector>& matrix) { + const int n = matrix.size(); + + for (int i = 0; i < n; ++i) // 沿着副对角线反转 + for (int j = 0; j < n - i; ++j) + swap(matrix[i][j], matrix[n - 1 - j][n - 1 - i]); + + for (int i = 0; i < n / 2; ++i) // 沿着水平中线反转 + for (int j = 0; j < n; ++j) + swap(matrix[i][j], matrix[n - 1 - i][j]); + } +}; +\end{Code} + +\subsubsection{代码2} +\begin{Code} +// LeetCode, Rotate Image +// 思路 2,时间复杂度O(n^2),空间复杂度O(1) +class Solution { +public: + void rotate(vector>& matrix) { + const int n = matrix.size(); + + for (int i = 0; i < n / 2; ++i) // 沿着水平中线反转 + for (int j = 0; j < n; ++j) + swap(matrix[i][j], matrix[n - 1 - i][j]); + + for (int i = 0; i < n; ++i) // 沿着主对角线反转 + for (int j = i + 1; j < n; ++j) + swap(matrix[i][j], matrix[j][i]); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\subsection{Plus One} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:plus-one} + + +\subsubsection{描述} +Given a number represented as an array of digits, plus one to the number. + + +\subsubsection{分析} +高精度加法。 + + +\subsubsection{代码1} +\begin{Code} +// LeetCode, Plus One +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + vector plusOne(vector &digits) { + add(digits, 1); + return digits; + } +private: + // 0 <= digit <= 9 + void add(vector &digits, int digit) { + int c = digit; // carry, 进位 + + for (auto it = digits.rbegin(); it != digits.rend(); ++it) { + *it += c; + c = *it / 10; + *it %= 10; + } + + if (c > 0) digits.insert(digits.begin(), 1); + } +}; +\end{Code} + + +\subsubsection{代码2} +\begin{Code} +// LeetCode, Plus One +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + vector plusOne(vector &digits) { + add(digits, 1); + return digits; + } +private: + // 0 <= digit <= 9 + void add(vector &digits, int digit) { + int c = digit; // carry, 进位 + + for_each(digits.rbegin(), digits.rend(), [&c](int &d){ + d += c; + c = d / 10; + d %= 10; + }); + + if (c > 0) digits.insert(digits.begin(), 1); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\subsection{Climbing Stairs} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:climbing-stairs} + + +\subsubsection{描述} +You are climbing a stair case. It takes $n$ steps to reach to the top. + +Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top? + + +\subsubsection{分析} +设$f(n)$表示爬$n$阶楼梯的不同方法数,为了爬到第$n$阶楼梯,有两个选择: +\begindot +\item 从第$n-1$阶前进1步; +\item 从第$n-1$阶前进2步; +\myenddot +因此,有$f(n)=f(n-1)+f(n-2)$。 + +这是一个斐波那契数列。 + +方法1,递归,太慢;方法2,迭代。 + +方法3,数学公式。斐波那契数列的通项公式为 $a_n=\dfrac{1}{\sqrt{5}}\left[\left(\dfrac{1+\sqrt{5}}{2}\right)^n-\left(\dfrac{1-\sqrt{5}}{2}\right)^n\right]$。 + + +\subsubsection{迭代} +\begin{Code} +// LeetCode, Climbing Stairs +// 迭代,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int climbStairs(int n) { + int prev = 0; + int cur = 1; + for(int i = 1; i <= n ; ++i){ + int tmp = cur; + cur += prev; + prev = tmp; + } + return cur; + } +}; +\end{Code} + + +\subsubsection{数学公式} +\begin{Code} +// LeetCode, Climbing Stairs +// 数学公式,时间复杂度O(1),空间复杂度O(1) +class Solution { + public: + int climbStairs(int n) { + const double s = sqrt(5); + return floor((pow((1+s)/2, n+1) + pow((1-s)/2, n+1))/s + 0.5); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Decode Ways, 见 \S \ref{sec:decode-ways} +\myenddot + + +\subsection{Gray Code} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:gray-code} + + +\subsubsection{描述} +The gray code is a binary numeral system where two successive values differ in only one bit. + +Given a non-negative integer $n$ representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0. + +For example, given $n = 2$, return \fn{[0,1,3,2]}. Its gray code sequence is: +\begin{Code} +00 - 0 +01 - 1 +11 - 3 +10 - 2 +\end{Code} + +Note: +\begindot +\item For a given $n$, a gray code sequence is not uniquely defined. +\item For example, \fn{[0,2,3,1]} is also a valid gray code sequence according to the above definition. +\item For now, the judge is able to judge based on one instance of gray code sequence. Sorry about that. +\myenddot + + +\subsubsection{分析} +格雷码(Gray Code)的定义请参考 \myurl{http://en.wikipedia.org/wiki/Gray_code} + +\textbf{自然二进制码转换为格雷码:$g_0=b_0, g_i=b_i \oplus b_{i-1}$} + +保留自然二进制码的最高位作为格雷码的最高位,格雷码次高位为二进制码的高位与次高位异或,其余各位与次高位的求法类似。例如,将自然二进制码1001,转换为格雷码的过程是:保留最高位;然后将第1位的1和第2位的0异或,得到1,作为格雷码的第2位;将第2位的0和第3位的0异或,得到0,作为格雷码的第3位;将第3位的0和第4位的1异或,得到1,作为格雷码的第4位,最终,格雷码为1101。 + +\textbf{格雷码转换为自然二进制码:$b_0=g_0, b_i=g_i \oplus b_{i-1}$} + +保留格雷码的最高位作为自然二进制码的最高位,次高位为自然二进制高位与格雷码次高位异或,其余各位与次高位的求法类似。例如,将格雷码1000转换为自然二进制码的过程是:保留最高位1,作为自然二进制码的最高位;然后将自然二进制码的第1位1和格雷码的第2位0异或,得到1,作为自然二进制码的第2位;将自然二进制码的第2位1和格雷码的第3位0异或,得到1,作为自然二进制码的第3位;将自然二进制码的第3位1和格雷码的第4位0异或,得到1,作为自然二进制码的第4位,最终,自然二进制码为1111。 + +格雷码有\textbf{数学公式},整数$n$的格雷码是$n \oplus (n/2)$。 + +这题要求生成$n$比特的所有格雷码。 + +方法1,最简单的方法,利用数学公式,对从 $0\sim2^n-1$的所有整数,转化为格雷码。 + +方法2,$n$比特的格雷码,可以递归地从$n-1$比特的格雷码生成。如图\S \ref{fig:gray-code-construction}所示。 + +\begin{center} +\includegraphics[width=160pt]{gray-code-construction.png}\\ +\figcaption{The first few steps of the reflect-and-prefix method.}\label{fig:gray-code-construction} +\end{center} + + +\subsubsection{数学公式} +\begin{Code} +// LeetCode, Gray Code +// 数学公式,时间复杂度O(2^n),空间复杂度O(1) +class Solution { +public: + vector grayCode(int n) { + vector result; + const size_t size = 1 << n; // 2^n + result.reserve(size); + for (size_t i = 0; i < size; ++i) + result.push_back(binary_to_gray(i)); + return result; + } +private: + static unsigned int binary_to_gray(unsigned int n) { + return n ^ (n >> 1); + } +}; +\end{Code} + + +\subsubsection{Reflect-and-prefix method} +\begin{Code} +// LeetCode, Gray Code +// reflect-and-prefix method +// 时间复杂度O(2^n),空间复杂度O(1) +class Solution { +public: + vector grayCode(int n) { + vector result; + result.reserve(1<= 0; j--) // 要反着遍历,才能对称 + result.push_back(highest_bit | result[j]); + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\subsection{Set Matrix Zeroes} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:set-matrix-zeroes} + + +\subsubsection{描述} +Given a $m \times n$ matrix, if an element is 0, set its entire row and column to 0. Do it in place. + +\textbf{Follow up:} +Did you use extra space? + +A straight forward solution using $O(mn)$ space is probably a bad idea. + +A simple improvement uses $O(m + n)$ space, but still not the best solution. + +Could you devise a constant space solution? + + +\subsubsection{分析} +$O(m+n)$空间的方法很简单,设置两个bool数组,记录每行和每列是否存在0。 + +想要常数空间,可以复用第一行和第一列。 + + +\subsubsection{代码1} +\begin{Code} +// LeetCode, Set Matrix Zeroes +// 时间复杂度O(m*n),空间复杂度O(m+n) +class Solution { +public: + void setZeroes(vector > &matrix) { + const size_t m = matrix.size(); + const size_t n = matrix[0].size(); + vector row(m, false); // 标记该行是否存在0 + vector col(n, false); // 标记该列是否存在0 + + for (size_t i = 0; i < m; ++i) { + for (size_t j = 0; j < n; ++j) { + if (matrix[i][j] == 0) { + row[i] = col[j] = true; + } + } + } + + for (size_t i = 0; i < m; ++i) { + if (row[i]) + fill(&matrix[i][0], &matrix[i][0] + n, 0); + } + for (size_t j = 0; j < n; ++j) { + if (col[j]) { + for (size_t i = 0; i < m; ++i) { + matrix[i][j] = 0; + } + } + } + } +}; +\end{Code} + + +\subsubsection{代码2} +\begin{Code} +// LeetCode, Set Matrix Zeroes +// 时间复杂度O(m*n),空间复杂度O(1) +class Solution { +public: + void setZeroes(vector > &matrix) { + const size_t m = matrix.size(); + const size_t n = matrix[0].size(); + bool row_has_zero = false; // 第一行是否存在 0 + bool col_has_zero = false; // 第一列是否存在 0 + + for (size_t i = 0; i < n; i++) + if (matrix[0][i] == 0) { + row_has_zero = true; + break; + } + + for (size_t i = 0; i < m; i++) + if (matrix[i][0] == 0) { + col_has_zero = true; + break; + } + + for (size_t i = 1; i < m; i++) + for (size_t j = 1; j < n; j++) + if (matrix[i][j] == 0) { + matrix[0][j] = 0; + matrix[i][0] = 0; + } + for (size_t i = 1; i < m; i++) + for (size_t j = 1; j < n; j++) + if (matrix[i][0] == 0 || matrix[0][j] == 0) + matrix[i][j] = 0; + if (row_has_zero) + for (size_t i = 0; i < n; i++) + matrix[0][i] = 0; + if (col_has_zero) + for (size_t i = 0; i < m; i++) + matrix[i][0] = 0; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\subsection{Gas Station} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:gas-station} + + +\subsubsection{描述} +There are $N$ gas stations along a circular route, where the amount of gas at station $i$ is \fn{gas[i]}. + +You have a car with an unlimited gas tank and it costs \fn{cost[i]} of gas to travel from station $i$ to its next station ($i$+1). You begin the journey with an empty tank at one of the gas stations. + +Return the starting gas station's index if you can travel around the circuit once, otherwise return -1. + +Note: +The solution is guaranteed to be unique. + + +\subsubsection{分析} +首先想到的是$O(N^2)$的解法,对每个点进行模拟。 + +$O(N)$的解法是,设置两个变量,\fn{sum}判断当前的指针的有效性;\fn{total}则判断整个数组是否有解,有就返回通过\fn{sum}得到的下标,没有则返回-1。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Gas Station +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int canCompleteCircuit(vector &gas, vector &cost) { + int total = 0; + int j = -1; + for (int i = 0, sum = 0; i < gas.size(); ++i) { + sum += gas[i] - cost[i]; + total += gas[i] - cost[i]; + if (sum < 0) { + j = i; + sum = 0; + } + } + return total >= 0 ? j + 1 : -1; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\subsection{Candy} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:candy} + + +\subsubsection{描述} +There are $N$ children standing in a line. Each child is assigned a rating value. + +You are giving candies to these children subjected to the following requirements: +\begindot +\item Each child must have at least one candy. +\item Children with a higher rating get more candies than their neighbors. +\myenddot + +What is the minimum candies you must give? + + +\subsubsection{分析} +无 + + +\subsubsection{迭代版} +\begin{Code} +// LeetCode, Candy +// 时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + int candy(vector &ratings) { + const int n = ratings.size(); + vector increment(n); + + // 左右各扫描一遍 + for (int i = 1, inc = 1; i < n; i++) { + if (ratings[i] > ratings[i - 1]) + increment[i] = max(inc++, increment[i]); + else + inc = 1; + } + + for (int i = n - 2, inc = 1; i >= 0; i--) { + if (ratings[i] > ratings[i + 1]) + increment[i] = max(inc++, increment[i]); + else + inc = 1; + } + // 初始值为n,因为每个小朋友至少一颗糖 + return accumulate(&increment[0], &increment[0]+n, n); + } +}; +\end{Code} + + +\subsubsection{递归版} +\begin{Code} +// LeetCode, Candy +// 备忘录法,时间复杂度O(n),空间复杂度O(n) +// @author fancymouse (http://weibo.com/u/1928162822) +class Solution { +public: + int candy(const vector& ratings) { + vector f(ratings.size()); + int sum = 0; + for (int i = 0; i < ratings.size(); ++i) + sum += solve(ratings, f, i); + return sum; + } + int solve(const vector& ratings, vector& f, int i) { + if (f[i] == 0) { + f[i] = 1; + if (i > 0 && ratings[i] > ratings[i - 1]) + f[i] = max(f[i], solve(ratings, f, i - 1) + 1); + if (i < ratings.size() - 1 && ratings[i] > ratings[i + 1]) + f[i] = max(f[i], solve(ratings, f, i + 1) + 1); + } + return f[i]; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\subsection{Single Number} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:single-number} + + +\subsubsection{描述} +Given an array of integers, every element appears twice except for one. Find that single one. + +Note: +Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory? + + +\subsubsection{分析} +异或,不仅能处理两次的情况,只要出现偶数次,都可以清零。 + + +\subsubsection{代码1} +\begin{Code} +// LeetCode, Single Number +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int singleNumber(int A[], int n) { + int x = 0; + for (size_t i = 0; i < n; ++i) + x ^= A[i]; + return x; + } +}; +\end{Code} + + +\subsubsection{代码2} +\begin{Code} +// LeetCode, Single Number +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int singleNumber(int A[], int n) { + return accumulate(A, A + n, 0, bit_xor()); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Single Number II, 见 \S \ref{sec:single-number-ii} +\myenddot + + +\subsection{Single Number II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:single-number-ii} + + +\subsubsection{描述} +Given an array of integers, every element appears three times except for one. Find that single one. + +Note: +Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory? + + +\subsubsection{分析} +本题和上一题 Single Number,考察的是位运算。 + +方法1:创建一个长度为\fn{sizeof(int)}的数组\fn{count[sizeof(int)]},\fn{count[i]}表示在在$i$位出现的1的次数。如果\fn{count[i]}是3的整数倍,则忽略;否则就把该位取出来组成答案。 + +方法2:用\fn{one}记录到当前处理的元素为止,二进制1出现“1次”(mod 3 之后的 1)的有哪些二进制位;用\fn{two}记录到当前计算的变量为止,二进制1出现“2次”(mod 3 之后的 2)的有哪些二进制位。当\fn{one}和\fn{two}中的某一位同时为1时表示该二进制位上1出现了3次,此时需要清零。即\textbf{用二进制模拟三进制运算}。最终\fn{one}记录的是最终结果。 + +\subsubsection{代码1} +\begin{Code} +// LeetCode, Single Number II +// 方法1,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int singleNumber(int A[], int n) { + const int W = sizeof(int) * 8; // 一个整数的bit数,即整数字长 + int count[W]; // count[i]表示在在i位出现的1的次数 + fill_n(&count[0], W, 0); + for (int i = 0; i < n; i++) { + for (int j = 0; j < W; j++) { + count[j] += (A[i] >> j) & 1; + count[j] %= 3; + } + } + int result = 0; + for (int i = 0; i < W; i++) { + result += (count[i] << i); + } + return result; + } +}; +\end{Code} + + +\subsubsection{代码2} +\begin{Code} +// LeetCode, Single Number II +// 方法2,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int singleNumber(int A[], int n) { + int one = 0, two = 0, three = 0; + for (int i = 0; i < n; ++i) { + two |= (one & A[i]); + one ^= A[i]; + three = ~(one & two); + one &= three; + two &= three; + } + + return one; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Single Number, 见 \S \ref{sec:single-number} +\myenddot + + +\section{单链表} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +单链表节点的定义如下: +\begin{Code} +// 单链表节点 +struct ListNode { + int val; + ListNode *next; + ListNode(int x) : val(x), next(nullptr) { } +}; +\end{Code} + + +\subsection{Add Two Numbers} +\label{sec:add-two-numbers} + + +\subsubsection{描述} +You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list. + +Input: {\small \fontspec{Latin Modern Mono} (2 -> 4 -> 3) + (5 -> 6 -> 4)} + +Output: {\small \fontspec{Latin Modern Mono} 7 -> 0 -> 8} + + +\subsubsection{分析} +跟Add Binary(见 \S \ref{sec:add-binary})很类似 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Add Two Numbers +// 跟Add Binary 很类似 +// 时间复杂度O(m+n),空间复杂度O(1) +class Solution { +public: + ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) { + ListNode dummy(-1); // 头节点 + int carry = 0; + ListNode *prev = &dummy; + for (ListNode *pa = l1, *pb = l2; + pa != nullptr || pb != nullptr; + pa = pa == nullptr ? nullptr : pa->next, + pb = pb == nullptr ? nullptr : pb->next, + prev = prev->next) { + const int ai = pa == nullptr ? 0 : pa->val; + const int bi = pb == nullptr ? 0 : pb->val; + const int value = (ai + bi + carry) % 10; + carry = (ai + bi + carry) / 10; + prev->next = new ListNode(value); // 尾插法 + } + if (carry > 0) + prev->next = new ListNode(carry); + return dummy.next; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item Add Binary, 见 \S \ref{sec:add-binary} +\myenddot + + +\subsection{Reverse Linked List II} +\label{sec:reverse-linked-list-ii} + + +\subsubsection{描述} +Reverse a linked list from position $m$ to $n$. Do it in-place and in one-pass. + +For example: +Given \code{1->2->3->4->5->nullptr}, $m$ = 2 and $n$ = 4, + +return \code{1->4->3->2->5->nullptr}. + +Note: +Given m, n satisfy the following condition: +$1 \leq m \leq n \leq $ length of list. + + +\subsubsection{分析} +这题非常繁琐,有很多边界检查,15分钟内做到bug free很有难度! + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Reverse Linked List II +// 迭代版,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + ListNode *reverseBetween(ListNode *head, int m, int n) { + ListNode dummy(-1); + dummy.next = head; + + ListNode *prev = &dummy; + for (int i = 0; i < m-1; ++i) + prev = prev->next; + ListNode* const head2 = prev; + + prev = head2->next; + ListNode *cur = prev->next; + for (int i = m; i < n; ++i) { + prev->next = cur->next; + cur->next = head2->next; + head2->next = cur; // 头插法 + cur = prev->next; + } + + return dummy.next; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item 无 +\myenddot + + +\subsection{Partition List} +\label{sec:partition-list} + + +\subsubsection{描述} +Given a linked list and a value $x$, partition it such that all nodes less than $x$ come before nodes greater than or equal to $x$. + +You should preserve the original relative order of the nodes in each of the two partitions. + +For example, +Given \code{1->4->3->2->5->2} and \code{x = 3}, return \code{1->2->2->4->3->5}. + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Partition List +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + ListNode* partition(ListNode* head, int x) { + ListNode left_dummy(-1); // 头结点 + ListNode right_dummy(-1); // 头结点 + + auto left_cur = &left_dummy; + auto right_cur = &right_dummy; + + for (ListNode *cur = head; cur; cur = cur->next) { + if (cur->val < x) { + left_cur->next = cur; + left_cur = cur; + } else { + right_cur->next = cur; + right_cur = cur; + } + } + + left_cur->next = right_dummy.next; + right_cur->next = nullptr; + + return left_dummy.next; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item 无 +\myenddot + + +\subsection{Remove Duplicates from Sorted List} +\label{sec:remove-duplicates-from-sorted-list} + + +\subsubsection{描述} +Given a sorted linked list, delete all duplicates such that each element appear only once. + +For example, + +Given \code{1->1->2}, return \code{1->2}. + +Given \code{1->1->2->3->3}, return \code{1->2->3}. + + +\subsubsection{分析} +无 + + +\subsubsection{递归版} +\begin{Code} +// LeetCode, Remove Duplicates from Sorted List +// 递归版,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + ListNode *deleteDuplicates(ListNode *head) { + if (!head) return head; + ListNode dummy(head->val + 1); // 值只要跟head不同即可 + dummy.next = head; + + recur(&dummy, head); + return dummy.next; + } +private: + static void recur(ListNode *prev, ListNode *cur) { + if (cur == nullptr) return; + + if (prev->val == cur->val) { // 删除head + prev->next = cur->next; + delete cur; + recur(prev, prev->next); + } else { + recur(prev->next, cur->next); + } + } +}; +\end{Code} + + +\subsubsection{迭代版} +\begin{Code} +// LeetCode, Remove Duplicates from Sorted List +// 迭代版,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + ListNode *deleteDuplicates(ListNode *head) { + if (head == nullptr) return nullptr; + + for (ListNode *prev = head, *cur = head->next; cur; cur = cur->next) { + if (prev->val == cur->val) { + prev->next = cur->next; + delete cur; + } else { + prev = cur; + } + } + return head; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item Remove Duplicates from Sorted List II,见 \S \ref{sec:remove-duplicates-from-sorted-list-ii} +\myenddot + + +\subsection{Remove Duplicates from Sorted List II} +\label{sec:remove-duplicates-from-sorted-list-ii} + + +\subsubsection{描述} +Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list. + +For example, + +Given \code{1->2->3->3->4->4->5}, return \code{1->2->5}. + +Given \code{1->1->1->2->3}, return \code{2->3}. + + +\subsubsection{分析} +无 + + +\subsubsection{递归版} +\begin{Code} +// LeetCode, Remove Duplicates from Sorted List II +// 递归版,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + ListNode *deleteDuplicates(ListNode *head) { + if (!head || !head->next) return head; + + ListNode *p = head->next; + if (head->val == p->val) { + while (p && head->val == p->val) { + ListNode *tmp = p; + p = p->next; + delete tmp; + } + delete head; + return deleteDuplicates(p); + } else { + head->next = deleteDuplicates(head->next); + return head; + } + } +}; +\end{Code} + + +\subsubsection{迭代版} +\begin{Code} +// LeetCode, Remove Duplicates from Sorted List II +// 迭代版,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + ListNode *deleteDuplicates(ListNode *head) { + if (head == nullptr) return head; + + ListNode dummy(INT_MIN); // 头结点 + dummy.next = head; + ListNode *prev = &dummy, *cur = head; + while (cur != nullptr) { + bool duplicated = false; + while (cur->next != nullptr && cur->val == cur->next->val) { + duplicated = true; + ListNode *temp = cur; + cur = cur->next; + delete temp; + } + if (duplicated) { // 删除重复的最后一个元素 + ListNode *temp = cur; + cur = cur->next; + delete temp; + continue; + } + prev->next = cur; + prev = prev->next; + cur = cur->next; + } + prev->next = cur; + return dummy.next; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item Remove Duplicates from Sorted List,见 \S \ref{sec:remove-duplicates-from-sorted-list} +\myenddot + + +\subsection{Rotate List} +\label{sec:rotate-list} + + +\subsubsection{描述} +Given a list, rotate the list to the right by $k$ places, where $k$ is non-negative. + +For example: +Given \code{1->2->3->4->5->nullptr} and \code{k = 2}, return \code{4->5->1->2->3->nullptr}. + + +\subsubsection{分析} +先遍历一遍,得出链表长度$len$,注意$k$可能大于$len$,因此令$k \%= len$。将尾节点next指针指向首节点,形成一个环,接着往后跑$len-k$步,从这里断开,就是要求的结果了。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Remove Rotate List +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + ListNode *rotateRight(ListNode *head, int k) { + if (head == nullptr || k == 0) return head; + + int len = 1; + ListNode* p = head; + while (p->next) { // 求长度 + len++; + p = p->next; + } + k = len - k % len; + + p->next = head; // 首尾相连 + for(int step = 0; step < k; step++) { + p = p->next; //接着往后跑 + } + head = p->next; // 新的首节点 + p->next = nullptr; // 断开环 + return head; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item 无 +\myenddot + + +\subsection{Remove Nth Node From End of List} +\label{sec:remove-nth-node-from-end-of-list} + + +\subsubsection{描述} +Given a linked list, remove the $n^{th}$ node from the end of list and return its head. + +For example, Given linked list: \code{1->2->3->4->5}, and $n$ = 2. + +After removing the second node from the end, the linked list becomes \code{1->2->3->5}. + +Note: +\begindot +\item Given $n$ will always be valid. +\item Try to do this in one pass. +\myenddot + + +\subsubsection{分析} +设两个指针$p,q$,让$q$先走$n$步,然后$p$和$q$一起走,直到$q$走到尾节点,删除\fn{p->next}即可。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Remove Nth Node From End of List +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + ListNode *removeNthFromEnd(ListNode *head, int n) { + ListNode dummy{-1, head}; + ListNode *p = &dummy, *q = &dummy; + + for (int i = 0; i < n; i++) // q先走n步 + q = q->next; + + while(q->next) { // 一起走 + p = p->next; + q = q->next; + } + ListNode *tmp = p->next; + p->next = p->next->next; + delete tmp; + return dummy.next; + } +}; +\end{Code} + + +\subsubsection{相关题目} + +\begindot +\item 无 +\myenddot + + +\subsection{Swap Nodes in Pairs} +\label{sec:swap-nodes-in-pairs} + + +\subsubsection{描述} +Given a linked list, swap every two adjacent nodes and return its head. + +For example, +Given \code{1->2->3->4}, you should return the list as \code{2->1->4->3}. + +Your algorithm should use only constant space. You may \emph{not} modify the values in the list, only nodes itself can be changed. + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Swap Nodes in Pairs +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + ListNode *swapPairs(ListNode *head) { + if (head == nullptr || head->next == nullptr) return head; + ListNode dummy(-1); + dummy.next = head; + + for(ListNode *prev = &dummy, *cur = prev->next, *next = cur->next; + next; + prev = cur, cur = cur->next, next = cur ? cur->next: nullptr) { + prev->next = next; + cur->next = next->next; + next->next = cur; + } + return dummy.next; + } +}; +\end{Code} + +下面这种写法更简洁,但题目规定了不准这样做。 +\begin{Code} +// LeetCode, Swap Nodes in Pairs +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + ListNode* swapPairs(ListNode* head) { + ListNode* p = head; + + while (p && p->next) { + swap(p->val, p->next->val); + p = p->next->next; + } + + return head; + } +}; +\end{Code} + +\subsubsection{相关题目} + +\begindot +\item Reverse Nodes in k-Group, 见 \S \ref{sec:reverse-nodes-in-k-group} +\myenddot + + +\subsection{Reverse Nodes in k-Group} +\label{sec:reverse-nodes-in-k-group} + + +\subsubsection{描述} +Given a linked list, reverse the nodes of a linked list k at a time and return its modified list. + +If the number of nodes is not a multiple of $k$ then left-out nodes in the end should remain as it is. + +You may not alter the values in the nodes, only nodes itself may be changed. + +Only constant memory is allowed. + +For example, +Given this linked list: \code{1->2->3->4->5} + +For $k = 2$, you should return: \code{2->1->4->3->5} + +For $k = 3$, you should return: \code{3->2->1->4->5} + + +\subsubsection{分析} +无 + + +\subsubsection{递归版} +\begin{Code} +// LeetCode, Reverse Nodes in k-Group +// 递归版,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + ListNode *reverseKGroup(ListNode *head, int k) { + if (head == nullptr || head->next == nullptr || k < 2) + return head; + + ListNode *next_group = head; + for (int i = 0; i < k; ++i) { + if (next_group) + next_group = next_group->next; + else + return head; + } + // next_group is the head of next group + // new_next_group is the new head of next group after reversion + ListNode *new_next_group = reverseKGroup(next_group, k); + ListNode *prev = NULL, *cur = head; + while (cur != next_group) { + ListNode *next = cur->next; + cur->next = prev ? prev : new_next_group; + prev = cur; + cur = next; + } + return prev; // prev will be the new head of this group + } +}; +\end{Code} + + +\subsubsection{迭代版} +\begin{Code} +// LeetCode, Reverse Nodes in k-Group +// 迭代版,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + ListNode *reverseKGroup(ListNode *head, int k) { + if (head == nullptr || head->next == nullptr || k < 2) return head; + ListNode dummy(-1); + dummy.next = head; + + for(ListNode *prev = &dummy, *end = head; end; end = prev->next) { + for (int i = 1; i < k && end; i++) + end = end->next; + if (end == nullptr) break; // 不足 k 个 + + prev = reverse(prev, prev->next, end); + } + + return dummy.next; + } + + // prev 是 first 前一个元素, [begin, end] 闭区间,保证三者都不为 null + // 返回反转后的倒数第1个元素 + ListNode* reverse(ListNode *prev, ListNode *begin, ListNode *end) { + ListNode *end_next = end->next; + for (ListNode *p = begin, *cur = p->next, *next = cur->next; + cur != end_next; + p = cur, cur = next, next = next ? next->next : nullptr) { + cur->next = p; + } + begin->next = end_next; + prev->next = end; + return begin; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Swap Nodes in Pairs, 见 \S \ref{sec:swap-nodes-in-pairs} +\myenddot + + +\subsection{Copy List with Random Pointer} +\label{sec:copy-list-with-random-pointer} + + +\subsubsection{描述} +A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null. + +Return a deep copy of the list. + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Copy List with Random Pointer +// 两遍扫描,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + RandomListNode *copyRandomList(RandomListNode *head) { + for (RandomListNode* cur = head; cur != nullptr; ) { + RandomListNode* node = new RandomListNode(cur->label); + node->next = cur->next; + cur->next = node; + cur = node->next; + } + + for (RandomListNode* cur = head; cur != nullptr; ) { + if (cur->random != NULL) + cur->next->random = cur->random->next; + cur = cur->next->next; + } + + // 分拆两个单链表 + RandomListNode dummy(-1); + for (RandomListNode* cur = head, *new_cur = &dummy; + cur != nullptr; ) { + new_cur->next = cur->next; + new_cur = new_cur->next; + cur->next = cur->next->next; + cur = cur->next; + } + return dummy.next; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\subsection{Linked List Cycle} +\label{sec:Linked-List-Cycle} + + +\subsubsection{描述} +Given a linked list, determine if it has a cycle in it. + +Follow up: +Can you solve it without using extra space? + + +\subsubsection{分析} +最容易想到的方法是,用一个哈希表\fn{unordered_map visited},记录每个元素是否被访问过,一旦出现某个元素被重复访问,说明存在环。空间复杂度$O(n)$,时间复杂度$O(N)$。 + +最好的方法是时间复杂度$O(n)$,空间复杂度$O(1)$的。设置两个指针,一个快一个慢,快的指针每次走两步,慢的指针每次走一步,如果快指针和慢指针相遇,则说明有环。参考\myurl{ http://leetcode.com/2010/09/detecting-loop-in-singly-linked-list.html} + + +\subsubsection{代码} +\begin{Code} +//LeetCode, Linked List Cycle +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + bool hasCycle(ListNode *head) { + // 设置两个指针,一个快一个慢 + ListNode *slow = head, *fast = head; + while (fast && fast->next) { + slow = slow->next; + fast = fast->next->next; + if (slow == fast) return true; + } + return false; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Linked List Cycle II, 见 \S \ref{sec:Linked-List-Cycle-II} +\myenddot + + +\subsection{Linked List Cycle II} +\label{sec:Linked-List-Cycle-II} + + +\subsubsection{描述} +Given a linked list, return the node where the cycle begins. If there is no cycle, return \fn{null}. + +Follow up: +Can you solve it without using extra space? + + +\subsubsection{分析} +当fast与slow相遇时,slow肯定没有遍历完链表,而fast已经在环内循环了$n$圈($1 \leq n$)。假设slow走了$s$步,则fast走了$2s$步(fast步数还等于$s$加上在环上多转的$n$圈),设环长为$r$,则: +\begin{eqnarray} +2s &=& s + nr \nonumber \\ +s &=& nr \nonumber +\end{eqnarray} + +设整个链表长$L$,环入口点与相遇点距离为$a$,起点到环入口点的距离为$x$,则 +\begin{eqnarray} +x + a &=& nr = (n – 1)r +r = (n-1)r + L - x \nonumber \\ +x &=& (n-1)r + (L – x – a) \nonumber +\end{eqnarray} + +$L – x – a$为相遇点到环入口点的距离,由此可知,从链表头到环入口点等于$n-1$圈内环+相遇点到环入口点,于是我们可以从\fn{head}开始另设一个指针\fn{slow2},两个慢指针每次前进一步,它俩一定会在环入口点相遇。 + + +\subsubsection{代码} +\begin{Code} +//LeetCode, Linked List Cycle II +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + ListNode *detectCycle(ListNode *head) { + ListNode *slow = head, *fast = head; + while (fast && fast->next) { + slow = slow->next; + fast = fast->next->next; + if (slow == fast) { + ListNode *slow2 = head; + + while (slow2 != slow) { + slow2 = slow2->next; + slow = slow->next; + } + return slow2; + } + } + return nullptr; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Linked List Cycle, 见 \S \ref{sec:Linked-List-Cycle} +\myenddot + + +\subsection{Reorder List} +\label{sec:Reorder-List} + + +\subsubsection{描述} +Given a singly linked list $L: L_0 \rightarrow L_1 \rightarrow \cdots \rightarrow L_{n-1} \rightarrow L_n$, +reorder it to: $L_0 \rightarrow L_n \rightarrow L_1 \rightarrow L_{n-1} \rightarrow L_2 \rightarrow L_{n-2} \rightarrow \cdots$ + +You must do this in-place without altering the nodes' values. + +For example, +Given \fn{\{1,2,3,4\}}, reorder it to \fn{\{1,4,2,3\}}. + + +\subsubsection{分析} +题目规定要in-place,也就是说只能使用$O(1)$的空间。 + +可以找到中间节点,断开,把后半截单链表reverse一下,再合并两个单链表。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Reorder List +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + void reorderList(ListNode *head) { + if (head == nullptr || head->next == nullptr) return; + + ListNode *slow = head, *fast = head, *prev = nullptr; + while (fast && fast->next) { + prev = slow; + slow = slow->next; + fast = fast->next->next; + } + prev->next = nullptr; // cut at middle + + slow = reverse(slow); + + // merge two lists + ListNode *curr = head; + while (curr->next) { + ListNode *tmp = curr->next; + curr->next = slow; + slow = slow->next; + curr->next->next = tmp; + curr = tmp; + } + curr->next = slow; + } + + ListNode* reverse(ListNode *head) { + if (head == nullptr || head->next == nullptr) return head; + + ListNode *prev = head; + for (ListNode *curr = head->next, *next = curr->next; curr; + prev = curr, curr = next, next = next ? next->next : nullptr) { + curr->next = prev; + } + head->next = nullptr; + return prev; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\subsection{LRU Cache} +\label{sec:LRU-Cachet} + + +\subsubsection{描述} +Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set. + +\fn{get(key)} - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1. + +\fn{set(key, value)} - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item. + + +\subsubsection{分析} +为了使查找、插入和删除都有较高的性能,我们使用一个双向链表(\fn{std::list})和一个哈希表(\fn{std::unordered_map}),因为: +\begin{itemize} +\item{哈希表保存每个节点的地址,可以基本保证在$O(1)$时间内查找节点} +\item{双向链表插入和删除效率高,单向链表插入和删除时,还要查找节点的前驱节点} +\end{itemize} + +具体实现细节: +\begin{itemize} +\item{越靠近链表头部,表示节点上次访问距离现在时间最短,尾部的节点表示最近访问最少} +\item{访问节点时,如果节点存在,把该节点交换到链表头部,同时更新hash表中该节点的地址} +\item{插入节点时,如果cache的size达到了上限capacity,则删除尾部节点,同时要在hash表中删除对应的项;新节点插入链表头部} +\end{itemize} + + +\subsubsection{代码} +\begin{Code} +// LeetCode, LRU Cache +// 时间复杂度O(logn),空间复杂度O(n) +class LRUCache{ +private: + struct CacheNode { + int key; + int value; + CacheNode(int k, int v) :key(k), value(v){} + }; +public: + LRUCache(int capacity) { + this->capacity = capacity; + } + + int get(int key) { + if (cacheMap.find(key) == cacheMap.end()) return -1; + + // 把当前访问的节点移到链表头部,并且更新map中该节点的地址 + cacheList.splice(cacheList.begin(), cacheList, cacheMap[key]); + cacheMap[key] = cacheList.begin(); + return cacheMap[key]->value; + } + + void set(int key, int value) { + if (cacheMap.find(key) == cacheMap.end()) { + if (cacheList.size() == capacity) { //删除链表尾部节点(最少访问的节点) + cacheMap.erase(cacheList.back().key); + cacheList.pop_back(); + } + // 插入新节点到链表头部, 并且在map中增加该节点 + cacheList.push_front(CacheNode(key, value)); + cacheMap[key] = cacheList.begin(); + } else { + //更新节点的值,把当前访问的节点移到链表头部,并且更新map中该节点的地址 + cacheMap[key]->value = value; + cacheList.splice(cacheList.begin(), cacheList, cacheMap[key]); + cacheMap[key] = cacheList.begin(); + } + } +private: + list cacheList; + unordered_map::iterator> cacheMap; + int capacity; +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + diff --git a/leetcode/C++/chapSearching.tex b/leetcode/C++/chapSearching.tex new file mode 100644 index 00000000..e008d75b --- /dev/null +++ b/leetcode/C++/chapSearching.tex @@ -0,0 +1,210 @@ +\chapter{查找} + + +\section{Search for a Range} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:search-for-a-range} + + +\subsubsection{描述} +Given a sorted array of integers, find the starting and ending position of a given target value. + +Your algorithm's runtime complexity must be in the order of $O(\log n)$. + +If the target is not found in the array, return \code{[-1, -1]}. + +For example, +Given \code{[5, 7, 7, 8, 8, 10]} and target value 8, +return \code{[3, 4]}. + + +\subsubsection{分析} +已经排好了序,用二分查找。 + + +\subsubsection{使用STL} +\begin{Code} +// LeetCode, Search for a Range +// 偷懒的做法,使用STL +// 时间复杂度O(logn),空间复杂度O(1) +class Solution { +public: + vector searchRange(int A[], int n, int target) { + const int l = distance(A, lower_bound(A, A + n, target)); + const int u = distance(A, prev(upper_bound(A, A + n, target))); + if (A[l] != target) // not found + return vector { -1, -1 }; + else + return vector { l, u }; + } +}; +\end{Code} + + +\subsubsection{重新实现 lower_bound 和 upper_bound} +\begin{Code} +// LeetCode, Search for a Range +// 重新实现 lower_bound 和 upper_bound +// 时间复杂度O(logn),空间复杂度O(1) +class Solution { +public: + vector searchRange (int A[], int n, int target) { + auto lower = lower_bound(A, A + n, target); + auto uppper = upper_bound(lower, A + n, target); + + if (lower == A + n || *lower != target) + return vector { -1, -1 }; + else + return vector {distance(A, lower), distance(A, prev(uppper))}; + } + + template + ForwardIterator lower_bound (ForwardIterator first, + ForwardIterator last, T value) { + while (first != last) { + auto mid = next(first, distance(first, last) / 2); + + if (value > *mid) first = ++mid; + else last = mid; + } + + return first; + } + + template + ForwardIterator upper_bound (ForwardIterator first, + ForwardIterator last, T value) { + while (first != last) { + auto mid = next(first, distance (first, last) / 2); + + if (value >= *mid) first = ++mid; // 与 lower_bound 仅此不同 + else last = mid; + } + + return first; + } +}; +\end{Code} + +\subsubsection{相关题目} +\begindot +\item Search Insert Position, 见 \S \ref{sec:search-insert-position} +\myenddot + + +\section{Search Insert Position} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:search-insert-position} + + +\subsubsection{描述} +Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order. + +You may assume no duplicates in the array. + +Here are few examples. +\begin{Code} +[1,3,5,6], 5 → 2 +[1,3,5,6], 2 → 1 +[1,3,5,6], 7 → 4 +[1,3,5,6], 0 → 0 +\end{Code} + + +\subsubsection{分析} +即\fn{std::lower_bound()}。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Search Insert Position +// 重新实现 lower_bound +// 时间复杂度O(logn),空间复杂度O(1) +class Solution { +public: + int searchInsert(int A[], int n, int target) { + return lower_bound(A, A + n, target) - A; + } + + template + ForwardIterator lower_bound (ForwardIterator first, + ForwardIterator last, T value) { + while (first != last) { + auto mid = next(first, distance(first, last) / 2); + + if (value > *mid) first = ++mid; + else last = mid; + } + + return first; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Search for a Range, 见 \S \ref{sec:search-for-a-range} +\myenddot + + +\section{Search a 2D Matrix} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:search-a-2d-matrix} + + +\subsubsection{描述} +Write an efficient algorithm that searches for a value in an $m \times n$ matrix. This matrix has the following properties: +\begindot +\item Integers in each row are sorted from left to right. +\item The first integer of each row is greater than the last integer of the previous row. +\myenddot + +For example, Consider the following matrix: +\begin{Code} +[ + [1, 3, 5, 7], + [10, 11, 16, 20], + [23, 30, 34, 50] +] +\end{Code} +Given \fn{target = 3}, return true. + + +\subsubsection{分析} +二分查找。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Search a 2D Matrix +// 时间复杂度O(logn),空间复杂度O(1) +class Solution { +public: + bool searchMatrix(const vector>& matrix, int target) { + if (matrix.empty()) return false; + const size_t m = matrix.size(); + const size_t n = matrix.front().size(); + + int first = 0; + int last = m * n; + + while (first < last) { + int mid = first + (last - first) / 2; + int value = matrix[mid / n][mid % n]; + + if (value == target) + return true; + else if (value < target) + first = mid + 1; + else + last = mid; + } + + return false; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot diff --git a/leetcode/C++/chapSorting.tex b/leetcode/C++/chapSorting.tex new file mode 100644 index 00000000..a1192eb5 --- /dev/null +++ b/leetcode/C++/chapSorting.tex @@ -0,0 +1,423 @@ +\chapter{排序} + +\section{Merge Sorted Array} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:merge-sorted-array} + + +\subsubsection{描述} +Given two sorted integer arrays A and B, merge B into A as one sorted array. + +Note: +You may assume that A has enough space to hold additional elements from B. The number of elements initialized in A and B are m and n respectively. + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +//LeetCode, Merge Sorted Array +// 时间复杂度O(m+n),空间复杂度O(1) +class Solution { +public: + void merge(int A[], int m, int B[], int n) { + int ia = m - 1, ib = n - 1, icur = m + n - 1; + while(ia >= 0 && ib >= 0) { + A[icur--] = A[ia] >= B[ib] ? A[ia--] : B[ib--]; + } + while(ib >= 0) { + A[icur--] = B[ib--]; + } + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Merge Two Sorted Lists,见 \S \ref{sec:merge-two-sorted-lists} +\item Merge k Sorted Lists,见 \S \ref{sec:merge-k-sorted-lists} +\myenddot + + +\section{Merge Two Sorted Lists} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:merge-two-sorted-lists} + + +\subsubsection{描述} +Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists. + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +//LeetCode, Merge Two Sorted Lists +// 时间复杂度O(min(m,n)),空间复杂度O(1) +class Solution { +public: + ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) { + if (l1 == nullptr) return l2; + if (l2 == nullptr) return l1; + ListNode dummy(-1); + ListNode *p = &dummy; + for (; l1 != nullptr && l2 != nullptr; p = p->next) { + if (l1->val > l2->val) { p->next = l2; l2 = l2->next; } + else { p->next = l1; l1 = l1->next; } + } + p->next = l1 != nullptr ? l1 : l2; + return dummy.next; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Merge Sorted Array \S \ref{sec:merge-sorted-array} +\item Merge k Sorted Lists,见 \S \ref{sec:merge-k-sorted-lists} +\myenddot + + +\section{Merge k Sorted Lists} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:merge-k-sorted-lists} + + +\subsubsection{描述} +Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. + + +\subsubsection{分析} +可以复用Merge Two Sorted Lists(见 \S \ref{sec:merge-two-sorted-lists})的函数 + + +\subsubsection{代码} +\begin{Code} +//LeetCode, Merge k Sorted Lists +// 时间复杂度O(n1+n2+...),空间复杂度O(1) +class Solution { +public: + ListNode *mergeKLists(vector &lists) { + if (lists.size() == 0) return nullptr; + + ListNode *p = lists[0]; + for (int i = 1; i < lists.size(); i++) { + p = mergeTwoLists(p, lists[i]); + } + return p; + } + + // Merge Two Sorted Lists + ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) { + ListNode head(-1); + for (ListNode* p = &head; l1 != nullptr || l2 != nullptr; p = p->next) { + int val1 = l1 == nullptr ? INT_MAX : l1->val; + int val2 = l2 == nullptr ? INT_MAX : l2->val; + if (val1 <= val2) { + p->next = l1; + l1 = l1->next; + } else { + p->next = l2; + l2 = l2->next; + } + } + return head.next; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Merge Sorted Array \S \ref{sec:merge-sorted-array} +\item Merge Two Sorted Lists,见 \S \ref{sec:merge-two-sorted-lists} +\myenddot + + +\section{Insertion Sort List} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:Insertion-Sort-List} + + +\subsubsection{描述} +Sort a linked list using insertion sort. + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Insertion Sort List +// 时间复杂度O(n^2),空间复杂度O(1) +class Solution { +public: + ListNode *insertionSortList(ListNode *head) { + ListNode dummy(INT_MIN); + //dummy.next = head; + + for (ListNode *cur = head; cur != nullptr;) { + auto pos = findInsertPos(&dummy, cur->val); + ListNode *tmp = cur->next; + cur->next = pos->next; + pos->next = cur; + cur = tmp; + } + return dummy.next; + } + + ListNode* findInsertPos(ListNode *head, int x) { + ListNode *pre = nullptr; + for (ListNode *cur = head; cur != nullptr && cur->val <= x; + pre = cur, cur = cur->next) + ; + return pre; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Sort List, 见 \S \ref{sec:Sort-List} +\myenddot + + +\section{Sort List} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:Sort-List} + + +\subsubsection{描述} +Sort a linked list in $O(n log n)$ time using constant space complexity. + + +\subsubsection{分析} +常数空间且$O(nlogn)$,单链表适合用归并排序,双向链表适合用快速排序。本题可以复用 "Merge Two Sorted Lists" 的代码。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Sort List +// 归并排序,时间复杂度O(nlogn),空间复杂度O(1) +class Solution { +public: + ListNode *sortList(ListNode *head) { + if (head == NULL || head->next == NULL)return head; + + // 快慢指针找到中间节点 + ListNode *fast = head, *slow = head; + while (fast->next != NULL && fast->next->next != NULL) { + fast = fast->next->next; + slow = slow->next; + } + // 断开 + fast = slow; + slow = slow->next; + fast->next = NULL; + + ListNode *l1 = sortList(head); // 前半段排序 + ListNode *l2 = sortList(slow); // 后半段排序 + return mergeTwoLists(l1, l2); + } + + // Merge Two Sorted Lists + ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) { + ListNode dummy(-1); + for (ListNode* p = &dummy; l1 != nullptr || l2 != nullptr; p = p->next) { + int val1 = l1 == nullptr ? INT_MAX : l1->val; + int val2 = l2 == nullptr ? INT_MAX : l2->val; + if (val1 <= val2) { + p->next = l1; + l1 = l1->next; + } else { + p->next = l2; + l2 = l2->next; + } + } + return dummy.next; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Insertion Sort List,见 \S \ref{sec:Insertion-Sort-List} +\myenddot + + +\section{First Missing Positive} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:first-missing-positive} + + +\subsubsection{描述} +Given an unsorted integer array, find the first missing positive integer. + +For example, +Given \fn{[1,2,0]} return \fn{3}, +and \fn{[3,4,-1,1]} return \fn{2}. + +Your algorithm should run in $O(n)$ time and uses constant space. + + +\subsubsection{分析} +本质上是桶排序(bucket sort),每当\fn{A[i]!= i+1}的时候,将A[i]与A[A[i]-1]交换,直到无法交换为止,终止条件是 \fn{A[i]== A[A[i]-1]}。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, First Missing Positive +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int firstMissingPositive(int A[], int n) { + bucket_sort(A, n); + + for (int i = 0; i < n; ++i) + if (A[i] != (i + 1)) + return i + 1; + return n + 1; + } +private: + static void bucket_sort(int A[], int n) { + for (int i = 0; i < n; i++) { + while (A[i] != i + 1) { + if (A[i] <= 0 || A[i] > n || A[i] == A[A[i] - 1]) + break; + swap(A[i], A[A[i] - 1]); + } + } + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Sort Colors, 见 \S \ref{sec:sort-colors} +\myenddot + + +\section{Sort Colors} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:sort-colors} + + +\subsubsection{描述} +Given an array with $n$ objects colored red, white or blue, sort them so that objects of the same color are adjacent, with the colors in the order red, white and blue. + +Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively. + +Note: +You are not suppose to use the library's sort function for this problem. + +\textbf{Follow up:} + +A rather straight forward solution is a two-pass algorithm using counting sort. + +First, iterate the array counting number of 0's, 1's, and 2's, then overwrite array with total number of 0's, then 1's and followed by 2's. + +Could you come up with an one-pass algorithm using only constant space? + + +\subsubsection{分析} +由于0, 1, 2 非常紧凑,首先想到计数排序(counting sort),但需要扫描两遍,不符合题目要求。 + +由于只有三种颜色,可以设置两个index,一个是red的index,一个是blue的index,两边往中间走。时间复杂度$O(n)$,空间复杂度$O(1)$。 + +第3种思路,利用快速排序里 partition 的思想,第一次将数组按0分割,第二次按1分割,排序完毕,可以推广到$n$种颜色,每种颜色有重复元素的情况。 + + +\subsubsection{代码1} +\begin{Code} +// LeetCode, Sort Colors +// Counting Sort +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + void sortColors(int A[], int n) { + int counts[3] = { 0 }; // 记录每个颜色出现的次数 + + for (int i = 0; i < n; i++) + counts[A[i]]++; + + for (int i = 0, index = 0; i < 3; i++) + for (int j = 0; j < counts[i]; j++) + A[index++] = i; + + } +}; +\end{Code} + + +\subsubsection{代码2} +\begin{Code} +// LeetCode, Sort Colors +// 双指针,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + void sortColors(int A[], int n) { + // 一个是red的index,一个是blue的index,两边往中间走 + int red = 0, blue = n - 1; + + for (int i = 0; i < blue + 1;) { + if (A[i] == 0) + swap(A[i++], A[red++]); + else if (A[i] == 2) + swap(A[i], A[blue--]); + else + i++; + } + } +}; +\end{Code} + + +\subsubsection{代码3} +\begin{Code} +// LeetCode, Sort Colors +// use partition() +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + void sortColors(int A[], int n) { + partition(partition(A, A + n, bind1st(equal_to(), 0)), A + n, + bind1st(equal_to(), 1)); + } +}; +\end{Code} + + +\subsubsection{代码4} +\begin{Code} +// LeetCode, Sort Colors +// 重新实现 partition() +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + void sortColors(int A[], int n) { + partition(partition(A, A + n, bind1st(equal_to(), 0)), A + n, + bind1st(equal_to(), 1)); + } +private: + template + ForwardIterator partition(ForwardIterator first, ForwardIterator last, + UnaryPredicate pred) { + auto pos = first; + + for (; first != last; ++first) + if (pred(*first)) + swap(*first, *pos++); + + return pos; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item First Missing Positive, 见 \S \ref{sec:first-missing-positive} +\myenddot diff --git a/leetcode/C++/chapStackAndQueue.tex b/leetcode/C++/chapStackAndQueue.tex new file mode 100644 index 00000000..db1c1a0b --- /dev/null +++ b/leetcode/C++/chapStackAndQueue.tex @@ -0,0 +1,334 @@ +\chapter{栈和队列} + + +\section{栈} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +\subsection{Valid Parentheses} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:valid-parentheses} + + +\subsubsection{描述} +Given a string containing just the characters \code{'(', ')', '\{', '\}', '['} and \code{']'}, determine if the input string is valid. + +The brackets must close in the correct order, \code{"()"} and \code{"()[]{}"} are all valid but \code{"(]"} and \code{"([)]"} are not. + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Valid Parentheses +// 时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + bool isValid (string const& s) { + string left = "([{"; + string right = ")]}"; + stack stk; + + for (auto c : s) { + if (left.find(c) != string::npos) { + stk.push (c); + } else { + if (stk.empty () || stk.top () != left[right.find (c)]) + return false; + else + stk.pop (); + } + } + return stk.empty(); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Generate Parentheses, 见 \S \ref{sec:generate-parentheses} +\item Longest Valid Parentheses, 见 \S \ref{sec:longest-valid-parentheses} +\myenddot + + +\subsection{Longest Valid Parentheses} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:longest-valid-parentheses} + + +\subsubsection{描述} +Given a string containing just the characters \code{'('} and \code{')'}, find the length of the longest valid (well-formed) parentheses substring. + +For \code{"(()"}, the longest valid parentheses substring is \code{"()"}, which has length = 2. + +Another example is \code{")()())"}, where the longest valid parentheses substring is \code{"()()"}, which has length = 4. + + +\subsubsection{分析} +无 + + +\subsubsection{使用栈} +\begin{Code} +// LeetCode, Longest Valid Parenthese +// 使用栈,时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + int longestValidParentheses(string s) { + int max_len = 0, last = -1; // the position of the last ')' + stack lefts; // keep track of the positions of non-matching '('s + + for (int i = 0; i < s.size(); ++i) { + if (s[i] =='(') { + lefts.push(i); + } else { + if (lefts.empty()) { + // no matching left + last = i; + } else { + // find a matching pair + lefts.pop(); + if (lefts.empty()) { + max_len = max(max_len, i-last); + } else { + max_len = max(max_len, i-lefts.top()); + } + } + } + } + return max_len; + } +}; +\end{Code} + +\subsubsection{Dynamic Programming, One Pass} +\begin{Code} +// LeetCode, Longest Valid Parenthese +// 时间复杂度O(n),空间复杂度O(n) +// @author 一只杰森(http://weibo.com/wjson) +class Solution { +public: + int longestValidParentheses(string s) { + vector f(s.size(), 0); + int ret = 0; + for (int i = s.size() - 2; i >= 0; --i) { + int match = i + f[i + 1] + 1; + // case: "((...))" + if (s[i] == '(' && match < s.size() && s[match] == ')') { + f[i] = f[i + 1] + 2; + // if a valid sequence exist afterwards "((...))()" + if (match + 1 < s.size()) f[i] += f[match + 1]; + } + ret = max(ret, f[i]); + } + return ret; + } +}; +\end{Code} + + +\subsubsection{两遍扫描} +\begin{Code} +// LeetCode, Longest Valid Parenthese +// 两遍扫描,时间复杂度O(n),空间复杂度O(1) +// @author 曹鹏(http://weibo.com/cpcs) +class Solution { +public: + int longestValidParentheses(string s) { + int answer = 0, depth = 0, start = -1; + for (int i = 0; i < s.size(); ++i) { + if (s[i] == '(') { + ++depth; + } else { + --depth; + if (depth < 0) { + start = i; + depth = 0; + } else if (depth == 0) { + answer = max(answer, i - start); + } + } + } + + depth = 0; + start = s.size(); + for (int i = s.size() - 1; i >= 0; --i) { + if (s[i] == ')') { + ++depth; + } else { + --depth; + if (depth < 0) { + start = i; + depth = 0; + } else if (depth == 0) { + answer = max(answer, start - i); + } + } + } + return answer; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Valid Parentheses, 见 \S \ref{sec:valid-parentheses} +\item Generate Parentheses, 见 \S \ref{sec:generate-parentheses} +\myenddot + + +\subsection{Largest Rectangle in Histogram} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:largest-rectangle-in-histogram} + + +\subsubsection{描述} +Given $n$ non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram. + +\begin{center} +\includegraphics[width=120pt]{histogram.png}\\ +\figcaption{Above is a histogram where width of each bar is 1, given height = \fn{[2,1,5,6,2,3]}.}\label{fig:histogram} +\end{center} + +\begin{center} +\includegraphics[width=120pt]{histogram-area.png}\\ +\figcaption{The largest rectangle is shown in the shaded area, which has area = 10 unit.}\label{fig:histogram-area} +\end{center} + +For example, +Given height = \fn{[2,1,5,6,2,3]}, +return 10. + + +\subsubsection{分析} +简单的,类似于 Container With Most Water(\S \ref{sec:container-with-most-water}),对每个柱子,左右扩展,直到碰到比自己矮的,计算这个矩形的面积,用一个变量记录最大的面积,复杂度$O(n^2)$,会超时。 + +如图\S \ref{fig:histogram-area}所示,从左到右处理直方,当$i=4$时,小于当前栈顶(即直方3),对于直方3,无论后面还是前面的直方,都不可能得到比目前栈顶元素更高的高度了,处理掉直方3(计算从直方3到直方4之间的矩形的面积,然后从栈里弹出);对于直方2也是如此;直到碰到比直方4更矮的直方1。 + +这就意味着,可以维护一个递增的栈,每次比较栈顶与当前元素。如果当前元素大于栈顶元素,则入栈,否则合并现有栈,直至栈顶元素小于当前元素。结尾时入栈元素0,重复合并一次。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Largest Rectangle in Histogram +// 时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + int largestRectangleArea(vector &height) { + stack s; + height.push_back(0); + int result = 0; + for (int i = 0; i < height.size(); ) { + if (s.empty() || height[i] > height[s.top()]) + s.push(i++); + else { + int tmp = s.top(); + s.pop(); + result = max(result, + height[tmp] * (s.empty() ? i : i - s.top() - 1)); + } + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Trapping Rain Water, 见 \S \ref{sec:trapping-rain-water} +\item Container With Most Water, 见 \S \ref{sec:container-with-most-water} +\myenddot + + +\subsection{Evaluate Reverse Polish Notation} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:Evaluate-Reverse-Polish-Notation} + + +\subsubsection{描述} +Evaluate the value of an arithmetic expression in Reverse Polish Notation. + +Valid operators are \fn{+, -, *, /}. Each operand may be an integer or another expression. + +Some examples: +\begin{Code} + ["2", "1", "+", "3", "*"] -> ((2 + 1) * 3) -> 9 + ["4", "13", "5", "/", "+"] -> (4 + (13 / 5)) -> 6 +\end{Code} + + +\subsubsection{分析} +无 + + +\subsubsection{递归版} +\begin{Code} +// LeetCode, Evaluate Reverse Polish Notation +// 递归,时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + int evalRPN(vector &tokens) { + int x, y; + auto token = tokens.back(); tokens.pop_back(); + if (is_operator(token)) { + y = evalRPN(tokens); + x = evalRPN(tokens); + if (token[0] == '+') x += y; + else if (token[0] == '-') x -= y; + else if (token[0] == '*') x *= y; + else x /= y; + } else { + size_t i; + x = stoi(token, &i); + } + return x; + } +private: + bool is_operator(const string &op) { + return op.size() == 1 && string("+-*/").find(op) != string::npos; + } +}; +\end{Code} + + +\subsubsection{迭代版} +\begin{Code} +// LeetCode, Max Points on a Line +// 迭代,时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + int evalRPN(vector &tokens) { + stack s; + for (auto token : tokens) { + if (!is_operator(token)) { + s.push(token); + } else { + int y = stoi(s.top()); + s.pop(); + int x = stoi(s.top()); + s.pop(); + if (token[0] == '+') x += y; + else if (token[0] == '-') x -= y; + else if (token[0] == '*') x *= y; + else x /= y; + s.push(to_string(x)); + } + } + return stoi(s.top()); + } +private: + bool is_operator(const string &op) { + return op.size() == 1 && string("+-*/").find(op) != string::npos; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{队列} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + diff --git a/leetcode/C++/chapString.tex b/leetcode/C++/chapString.tex new file mode 100644 index 00000000..f6cf67f5 --- /dev/null +++ b/leetcode/C++/chapString.tex @@ -0,0 +1,1174 @@ +\chapter{字符串} + + +\section{Valid Palindrome} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:valid-palindrome} + + +\subsubsection{描述} +Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases. + +For example,\\ +\code{"A man, a plan, a canal: Panama"} is a palindrome.\\ +\code{"race a car"} is not a palindrome. + +Note: +Have you consider that the string might be empty? This is a good question to ask during an interview. + +For the purpose of this problem, we define empty string as valid palindrome. + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +// Leet Code, Valid Palindrome +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + bool isPalindrome(string s) { + transform(s.begin(), s.end(), s.begin(), ::tolower); + auto left = s.begin(), right = prev(s.end()); + while (left < right) { + if (!::isalnum(*left)) ++left; + else if (!::isalnum(*right)) --right; + else if (*left != *right) return false; + else{ left++, right--; } + } + return true; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Palindrome Number, 见 \S \ref{sec:palindrome-number} +\myenddot + + +\section{Implement strStr()} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:strstr} + + +\subsubsection{描述} +Implement strStr(). + +Returns a pointer to the first occurrence of needle in haystack, or null if needle is not part of haystack. + + +\subsubsection{分析} +暴力算法的复杂度是 $O(m*n)$,代码如下。更高效的的算法有KMP算法、Boyer-Mooer算法和Rabin-Karp算法。面试中暴力算法足够了,一定要写得没有BUG。 + + +\subsubsection{暴力匹配} +\begin{Code} +// LeetCode, Implement strStr() +// 暴力解法,时间复杂度O(N*M),空间复杂度O(1) +class Solution { +public: + char *strStr(const char *haystack, const char *needle) { + // if needle is empty return the full string + if (!*needle) return (char*) haystack; + + const char *p1; + const char *p2; + const char *p1_advance = haystack; + for (p2 = &needle[1]; *p2; ++p2) { + p1_advance++; // advance p1_advance M-1 times + } + + for (p1 = haystack; *p1_advance; p1_advance++) { + char *p1_old = (char*) p1; + p2 = needle; + while (*p1 && *p2 && *p1 == *p2) { + p1++; + p2++; + } + if (!*p2) return p1_old; + + p1 = p1_old + 1; + } + return nullptr; + } +}; +\end{Code} + + +\subsubsection{KMP} +\begin{Code} +// LeetCode, Implement strStr() +// KMP,时间复杂度O(N+M),空间复杂度O(M) +class Solution { +public: + char *strStr(const char *haystack, const char *needle) { + int pos = kmp(haystack, needle); + if (pos == -1) return nullptr; + else return (char*)haystack + pos; + } +private: + /* + * @brief 计算部分匹配表,即next数组. + * + * @param[in] pattern 模式串 + * @param[out] next next数组 + * @return 无 + */ + static void compute_prefix(const char *pattern, int next[]) { + int i; + int j = -1; + const int m = strlen(pattern); + + next[0] = j; + for (i = 1; i < m; i++) { + while (j > -1 && pattern[j + 1] != pattern[i]) j = next[j]; + + if (pattern[i] == pattern[j + 1]) j++; + next[i] = j; + } + } + + /* + * @brief KMP算法. + * + * @param[in] text 文本 + * @param[in] pattern 模式串 + * @return 成功则返回第一次匹配的位置,失败则返回-1 + */ + static int kmp(const char *text, const char *pattern) { + int i; + int j = -1; + const int n = strlen(text); + const int m = strlen(pattern); + if (n == 0 && m == 0) return 0; /* "","" */ + if (m == 0) return 0; /* "a","" */ + int *next = (int*)malloc(sizeof(int) * m); + + compute_prefix(pattern, next); + + for (i = 0; i < n; i++) { + while (j > -1 && pattern[j + 1] != text[i]) j = next[j]; + + if (text[i] == pattern[j + 1]) j++; + if (j == m - 1) { + free(next); + return i-j; + } + } + + free(next); + return -1; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item String to Integer (atoi) ,见 \S \ref{sec:string-to-integer} +\myenddot + + +\section{String to Integer (atoi)} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:string-to-integer} + + +\subsubsection{描述} +Implement \fn{atoi} to convert a string to an integer. + +\textbf{Hint}: Carefully consider all possible input cases. If you want a challenge, please do not see below and ask yourself what are the possible input cases. + +\textbf{Notes}: It is intended for this problem to be specified vaguely (ie, no given input specs). You are responsible to gather all the input requirements up front. + +\textbf{Requirements for atoi}: + +The function first discards as many whitespace characters as necessary until the first non-whitespace character is found. Then, starting from this character, takes an optional initial plus or minus sign followed by as many numerical digits as possible, and interprets them as a numerical value. + +The string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function. + +If the first sequence of non-whitespace characters in str is not a valid integral number, or if no such sequence exists because either str is empty or it contains only whitespace characters, no conversion is performed. + +If no valid conversion could be performed, a zero value is returned. If the correct value is out of the range of representable values, \code{INT_MAX (2147483647)} or \code{INT_MIN (-2147483648)} is returned. + +\subsubsection{分析} +细节题。注意几个测试用例: +\begin{enumerate} +\item 不规则输入,但是有效,"-3924x8fc", " + 413", +\item 无效格式," ++c", " ++1" +\item 溢出数据,"2147483648" +\end{enumerate} + +\subsubsection{代码} +\begin{Code} +// LeetCode, String to Integer (atoi) +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int atoi(const char *str) { + int num = 0; + int sign = 1; + const int n = strlen(str); + int i = 0; + + while (str[i] == ' ' && i < n) i++; + + if (str[i] == '+') { + i++; + } else if (str[i] == '-') { + sign = -1; + i++; + } + + for (; i < n; i++) { + if (str[i] < '0' || str[i] > '9') + break; + if (num > INT_MAX / 10 || + (num == INT_MAX / 10 && + (str[i] - '0') > INT_MAX % 10)) { + return sign == -1 ? INT_MIN : INT_MAX; + } + num = num * 10 + str[i] - '0'; + } + return num * sign; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Implement strStr() ,见 \S \ref{sec:strstr} +\myenddot + + +\section{Add Binary} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:add-binary} + + +\subsubsection{描述} +Given two binary strings, return their sum (also a binary string). + +For example, +\begin{Code} +a = "11" +b = "1" +\end{Code} +Return {\small \fontspec{Latin Modern Mono} "100"}. + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +//LeetCode, Add Binary +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + string addBinary(string a, string b) { + string result; + const size_t n = a.size() > b.size() ? a.size() : b.size(); + reverse(a.begin(), a.end()); + reverse(b.begin(), b.end()); + int carry = 0; + for (size_t i = 0; i < n; i++) { + const int ai = i < a.size() ? a[i] - '0' : 0; + const int bi = i < b.size() ? b[i] - '0' : 0; + const int val = (ai + bi + carry) % 2; + carry = (ai + bi + carry) / 2; + result.insert(result.begin(), val + '0'); + } + if (carry == 1) { + result.insert(result.begin(), '1'); + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Add Two Numbers, 见 \S \ref{sec:add-two-numbers} +\myenddot + + +\section{Longest Palindromic Substring} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:longest-palindromic-substring} + + +\subsubsection{描述} +Given a string $S$, find the longest palindromic substring in $S$. You may assume that the maximum length of $S$ is 1000, and there exists one unique longest palindromic substring. + + +\subsubsection{分析} +最长回文子串,非常经典的题。 + +思路一:暴力枚举,以每个元素为中间元素,同时从左右出发,复杂度$O(n^2)$。 + +思路二:记忆化搜索,复杂度$O(n^2)$。设\fn{f[i][j]} 表示[i,j]之间的最长回文子串,递推方程如下: +\begin{Code} +f[i][j] = if (i == j) S[i] + if (S[i] == S[j] && f[i+1][j-1] == S[i+1][j-1]) S[i][j] + else max(f[i+1][j-1], f[i][j-1], f[i+1][j]) +\end{Code} + +思路三:动规,复杂度$O(n^2)$。设状态为\fn{f(i,j)},表示区间[i,j]是否为回文串,则状态转移方程为 +$$ +f(i,j)=\begin{cases} +true & ,i=j\\ +S[i]=S[j] & , j = i + 1 \\ +S[i]=S[j] \text{ and } f(i+1, j-1) & , j > i + 1 +\end{cases} +$$ + +思路三:Manacher’s Algorithm, 复杂度$O(n)$。详细解释见 \myurl{http://leetcode.com/2011/11/longest-palindromic-substring-part-ii.html} 。 + + +\subsubsection{备忘录法} +\begin{Code} +// LeetCode, Longest Palindromic Substring +// 备忘录法,会超时 +// 时间复杂度O(n^2),空间复杂度O(n^2) +typedef string::const_iterator Iterator; + +namespace std { +template<> +struct hash> { + size_t operator()(pair const& p) const { + return ((size_t) &(*p.first)) ^ ((size_t) &(*p.second)); + } +}; +} + +class Solution { +public: + string longestPalindrome(string const& s) { + cache.clear(); + return cachedLongestPalindrome(s.begin(), s.end()); + } + +private: + unordered_map, string> cache; + + string longestPalindrome(Iterator first, Iterator last) { + size_t length = distance(first, last); + + if (length < 2) return string(first, last); + + auto s = cachedLongestPalindrome(next(first), prev(last)); + + if (s.length() == length - 2 && *first == *prev(last)) + return string(first, last); + + auto s1 = cachedLongestPalindrome(next(first), last); + auto s2 = cachedLongestPalindrome(first, prev(last)); + + // return max(s, s1, s2) + if (s.size() > s1.size()) return s.size() > s2.size() ? s : s2; + else return s1.size() > s2.size() ? s1 : s2; + } + + string cachedLongestPalindrome(Iterator first, Iterator last) { + auto key = make_pair(first, last); + auto pos = cache.find(key); + + if (pos != cache.end()) return pos->second; + else return cache[key] = longestPalindrome(first, last); + } +}; +\end{Code} + + +\subsubsection{动规} +\begin{Code} +// LeetCode, Longest Palindromic Substring +// 动规,时间复杂度O(n^2),空间复杂度O(n^2) +class Solution { +public: + string longestPalindrome(string s) { + const int n = s.size(); + bool f[n][n]; + fill_n(&f[0][0], n * n, false); + // 用 vector 会超时 + //vector > f(n, vector(n, false)); + size_t max_len = 1, start = 0; // 最长回文子串的长度,起点 + + for (size_t i = 0; i < s.size(); i++) { + f[i][i] = true; + for (size_t j = 0; j < i; j++) { // [j, i] + f[j][i] = (s[j] == s[i] && (i - j < 2 || f[j + 1][i - 1])); + if (f[j][i] && max_len < (i - j + 1)) { + max_len = i - j + 1; + start = j; + } + } + } + return s.substr(start, max_len); + } +}; +\end{Code} + + +\subsubsection{Manacher’s Algorithm} +\begin{Code} +// LeetCode, Longest Palindromic Substring +// Manacher’s Algorithm +// 时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + // Transform S into T. + // For example, S = "abba", T = "^#a#b#b#a#$". + // ^ and $ signs are sentinels appended to each end to avoid bounds checking + string preProcess(string s) { + int n = s.length(); + if (n == 0) return "^$"; + + string ret = "^"; + for (int i = 0; i < n; i++) ret += "#" + s.substr(i, 1); + + ret += "#$"; + return ret; + } + + string longestPalindrome(string s) { + string T = preProcess(s); + const int n = T.length(); + // 以T[i]为中心,向左/右扩张的长度,不包含T[i]自己, + // 因此 P[i]是源字符串中回文串的长度 + int P[n]; + int C = 0, R = 0; + + for (int i = 1; i < n - 1; i++) { + int i_mirror = 2 * C - i; // equals to i' = C - (i-C) + + P[i] = (R > i) ? min(R - i, P[i_mirror]) : 0; + + // Attempt to expand palindrome centered at i + while (T[i + 1 + P[i]] == T[i - 1 - P[i]]) + P[i]++; + + // If palindrome centered at i expand past R, + // adjust center based on expanded palindrome. + if (i + P[i] > R) { + C = i; + R = i + P[i]; + } + } + + // Find the maximum element in P. + int max_len = 0; + int center_index = 0; + for (int i = 1; i < n - 1; i++) { + if (P[i] > max_len) { + max_len = P[i]; + center_index = i; + } + } + + return s.substr((center_index - 1 - max_len) / 2, max_len); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{Regular Expression Matching} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:regular-expression-matching} + + +\subsubsection{描述} +Implement regular expression matching with support for \fn{'.'} and \fn{'*'}. + +\fn{'.'} Matches any single character. +\fn{'*'} Matches zero or more of the preceding element. + +The matching should cover the entire input string (not partial). + +The function prototype should be: +\begin{Code} +bool isMatch(const char *s, const char *p) +\end{Code} + +Some examples: +\begin{Code} +isMatch("aa","a") → false +isMatch("aa","aa") → true +isMatch("aaa","aa") → false +isMatch("aa", "a*") → true +isMatch("aa", ".*") → true +isMatch("ab", ".*") → true +isMatch("aab", "c*a*b") → true +\end{Code} + + +\subsubsection{分析} +这是一道很有挑战的题。 + + +\subsubsection{递归版} +\begin{Code} +// LeetCode, Regular Expression Matching +// 递归版,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + bool isMatch(const char *s, const char *p) { + if (*p == '\0') return *s == '\0'; + + // next char is not '*', then must match current character + if (*(p + 1) != '*') { + if (*p == *s || (*p == '.' && *s != '\0')) + return isMatch(s + 1, p + 1); + else + return false; + } else { // next char is '*' + while (*p == *s || (*p == '.' && *s != '\0')) { + if (isMatch(s, p + 2)) + return true; + s++; + } + return isMatch(s, p + 2); + } + } +}; +\end{Code} + + +\subsubsection{迭代版} +\begin{Code} + +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Wildcard Matching, 见 \S \ref{sec:wildcard-matching} +\myenddot + + +\section{Wildcard Matching} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:wildcard-matching} + + +\subsubsection{描述} +Implement wildcard pattern matching with support for \fn{'?'} and \fn{'*'}. + +\fn{'?'} Matches any single character. +\fn{'*'} Matches any sequence of characters (including the empty sequence). + +The matching should cover the entire input string (not partial). + +The function prototype should be: +\begin{Code} +bool isMatch(const char *s, const char *p) +\end{Code} + +Some examples: +\begin{Code} +isMatch("aa","a") → false +isMatch("aa","aa") → true +isMatch("aaa","aa") → false +isMatch("aa", "*") → true +isMatch("aa", "a*") → true +isMatch("ab", "?*") → true +isMatch("aab", "c*a*b") → false +\end{Code} + + +\subsubsection{分析} +跟上一题很类似。 + +主要是\fn{'*'}的匹配问题。\fn{p}每遇到一个\fn{'*'},就保留住当前\fn{'*'}的坐标和\fn{s}的坐标,然后\fn{s}从前往后扫描,如果不成功,则\fn{s++},重新扫描。 + + +\subsubsection{递归版} +\begin{Code} +// LeetCode, Wildcard Matching +// 递归版,会超时,用于帮助理解题意 +// 时间复杂度O(n!*m!),空间复杂度O(n) +class Solution { +public: + bool isMatch(const char *s, const char *p) { + if (*p == '*') { + while (*p == '*') ++p; //skip continuous '*' + if (*p == '\0') return true; + while (*s != '\0' && !isMatch(s, p)) ++s; + + return *s != '\0'; + } + else if (*p == '\0' || *s == '\0') return *p == *s; + else if (*p == *s || *p == '?') return isMatch(++s, ++p); + else return false; + } +}; +\end{Code} + + +\subsubsection{迭代版} +\begin{Code} +// LeetCode, Wildcard Matching +// 迭代版,时间复杂度O(n*m),空间复杂度O(1) +class Solution { +public: + bool isMatch(const char *s, const char *p) { + bool star = false; + const char *str, *ptr; + for (str = s, ptr = p; *str != '\0'; str++, ptr++) { + switch (*ptr) { + case '?': + break; + case '*': + star = true; + s = str, p = ptr; + while (*p == '*') p++; //skip continuous '*' + if (*p == '\0') return true; + str = s - 1; + ptr = p - 1; + break; + default: + if (*str != *ptr) { + // 如果前面没有'*',则匹配不成功 + if (!star) return false; + s++; + str = s - 1; + ptr = p - 1; + } + } + } + while (*ptr == '*') ptr++; + return (*ptr == '\0'); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Regular Expression Matching, 见 \S \ref{sec:regular-expression-matching} +\myenddot + + +\section{Longest Common Prefix} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:longest-common-prefix} + + +\subsubsection{描述} +Write a function to find the longest common prefix string amongst an array of strings. + + +\subsubsection{分析} +从位置0开始,对每一个位置比较所有字符串,直到遇到一个不匹配。 + + +\subsubsection{纵向扫描} +\begin{Code} +// LeetCode, Longest Common Prefix +// 纵向扫描,从位置0开始,对每一个位置比较所有字符串,直到遇到一个不匹配 +// 时间复杂度O(n1+n2+...) +// @author 周倩 (http://weibo.com/zhouditty) +class Solution { +public: + string longestCommonPrefix(vector &strs) { + if (strs.empty()) return ""; + + for (int idx = 0; idx < strs[0].size(); ++idx) { // 纵向扫描 + for (int i = 1; i < strs.size(); ++i) { + if (strs[i][idx] != strs[0][idx]) return strs[0].substr(0,idx); + } + } + return strs[0]; + } +}; +\end{Code} + + +\subsubsection{横向扫描} +\begin{Code} +// LeetCode, Longest Common Prefix +// 横向扫描,每个字符串与第0个字符串,从左到右比较,直到遇到一个不匹配, +// 然后继续下一个字符串 +// 时间复杂度O(n1+n2+...) +class Solution { +public: + string longestCommonPrefix(vector &strs) { + if (strs.empty()) return ""; + + int right_most = strs[0].size() - 1; + for (size_t i = 1; i < strs.size(); i++) + for (int j = 0; j <= right_most; j++) + if (strs[i][j] != strs[0][j]) // 不会越界,请参考string::[]的文档 + right_most = j - 1; + + return strs[0].substr(0, right_most + 1); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{Valid Number} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:valid-number} + + +\subsubsection{描述} +Validate if a given string is numeric. + +Some examples: +\begin{Code} +"0" => true +" 0.1 " => true +"abc" => false +"1 a" => false +"2e10" => true +\end{Code} + +Note: It is intended for the problem statement to be ambiguous. You should gather all requirements up front before implementing one. + + +\subsubsection{分析} +细节实现题。 + +本题的功能与标准库中的\fn{strtod()}功能类似。 + + +\subsubsection{有限自动机} +\begin{Code} +// LeetCode, Valid Number +// @author 龚陆安 (http://weibo.com/luangong) +// finite automata,时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + bool isNumber(const char *s) { + enum InputType { + INVALID, // 0 + SPACE, // 1 + SIGN, // 2 + DIGIT, // 3 + DOT, // 4 + EXPONENT, // 5 + NUM_INPUTS // 6 + }; + const int transitionTable[][NUM_INPUTS] = { + -1, 0, 3, 1, 2, -1, // next states for state 0 + -1, 8, -1, 1, 4, 5, // next states for state 1 + -1, -1, -1, 4, -1, -1, // next states for state 2 + -1, -1, -1, 1, 2, -1, // next states for state 3 + -1, 8, -1, 4, -1, 5, // next states for state 4 + -1, -1, 6, 7, -1, -1, // next states for state 5 + -1, -1, -1, 7, -1, -1, // next states for state 6 + -1, 8, -1, 7, -1, -1, // next states for state 7 + -1, 8, -1, -1, -1, -1, // next states for state 8 + }; + + int state = 0; + for (; *s != '\0'; ++s) { + InputType inputType = INVALID; + if (isspace(*s)) + inputType = SPACE; + else if (*s == '+' || *s == '-') + inputType = SIGN; + else if (isdigit(*s)) + inputType = DIGIT; + else if (*s == '.') + inputType = DOT; + else if (*s == 'e' || *s == 'E') + inputType = EXPONENT; + + // Get next state from current state and input symbol + state = transitionTable[state][inputType]; + + // Invalid input + if (state == -1) return false; + } + // If the current state belongs to one of the accepting (final) states, + // then the number is valid + return state == 1 || state == 4 || state == 7 || state == 8; + + } +}; +\end{Code} + + +\subsubsection{使用strtod()} +\begin{Code} +// LeetCode, Valid Number +// @author 连城 (http://weibo.com/lianchengzju) +// 偷懒,直接用 strtod(),时间复杂度O(n) +class Solution { +public: + bool isNumber (char const* s) { + char* endptr; + strtod (s, &endptr); + + if (endptr == s) return false; + + for (; *endptr; ++endptr) + if (!isspace (*endptr)) return false; + + return true; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{Integer to Roman} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:integer-to-roman} + + +\subsubsection{描述} +Given an integer, convert it to a roman numeral. + +Input is guaranteed to be within the range from 1 to 3999. + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Integer to Roman +// 时间复杂度O(num),空间复杂度O(1) +class Solution { +public: + string intToRoman(int num) { + const int radix[] = {1000, 900, 500, 400, 100, 90, + 50, 40, 10, 9, 5, 4, 1}; + const string symbol[] = {"M", "CM", "D", "CD", "C", "XC", + "L", "XL", "X", "IX", "V", "IV", "I"}; + + string roman; + for (size_t i = 0; num > 0; ++i) { + int count = num / radix[i]; + num %= radix[i]; + for (; count > 0; --count) roman += symbol[i]; + } + return roman; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Roman to Integer, 见 \S \ref{sec:roman-to-integer} +\myenddot + + +\section{Roman to Integer} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:roman-to-integer} + + +\subsubsection{描述} +Given a roman numeral, convert it to an integer. + +Input is guaranteed to be within the range from 1 to 3999. + + +\subsubsection{分析} +从前往后扫描,用一个临时变量记录分段数字。 + +如果当前比前一个大,说明这一段的值应该是当前这个值减去上一个值。比如\fn{IV = 5 – 1};否则,将当前值加入到结果中,然后开始下一段记录。比如\fn{VI = 5 + 1, II=1+1} + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Roman to Integer +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + inline int map(const char c) { + switch (c) { + case 'I': return 1; + case 'V': return 5; + case 'X': return 10; + case 'L': return 50; + case 'C': return 100; + case 'D': return 500; + case 'M': return 1000; + default: return 0; + } + } + + int romanToInt(string s) { + int result = 0; + for (size_t i = 0; i < s.size(); i++) { + if (i > 0 && map(s[i]) > map(s[i - 1])) { + result += (map(s[i]) - 2 * map(s[i - 1])); + } else { + result += map(s[i]); + } + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Integer to Roman, 见 \S \ref{sec:integer-to-roman} +\myenddot + + +\section{Count and Say} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:count-and-say} + + +\subsubsection{描述} +The count-and-say sequence is the sequence of integers beginning as follows: +\begin{Code} +1, 11, 21, 1211, 111221, ... +\end{Code} + +\fn{1} is read off as \fn{"one 1"} or \fn{11}. + +\fn{11} is read off as \fn{"two 1s"} or \fn{21}. + +\fn{21} is read off as \fn{"one 2"}, then \fn{"one 1"} or \fn{1211}. + +Given an integer $n$, generate the nth sequence. + +Note: The sequence of integers will be represented as a string. + + +\subsubsection{分析} +模拟。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Count and Say +// @author 连城 (http://weibo.com/lianchengzju) +// 时间复杂度O(n^2),空间复杂度O(n) +class Solution { +public: + string countAndSay(int n) { + string s("1"); + + while (--n) + s = getNext(s); + + return s; + } + + string getNext(const string &s) { + stringstream ss; + + for (auto i = s.begin(); i != s.end(); ) { + auto j = find_if(i, s.end(), bind1st(not_equal_to(), *i)); + ss << distance(i, j) << *i; + i = j; + } + + return ss.str(); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{Anagrams} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:anagrams} + + +\subsubsection{描述} +Given an array of strings, return all groups of strings that are anagrams. + +Note: All inputs will be in lower-case. + + +\subsubsection{分析} +Anagram(回文构词法)是指打乱字母顺序从而得到新的单词,比如 \fn{"dormitory"} 打乱字母顺序会变成 \fn{"dirty room"} ,\fn{"tea"} 会变成\fn{"eat"}。 + +回文构词法有一个特点:单词里的字母的种类和数目没有改变,只是改变了字母的排列顺序。因此,将几个单词按照字母顺序排序后,若它们相等,则它们属于同一组 anagrams 。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Anagrams +// 时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + vector anagrams(vector &strs) { + unordered_map > group; + for (const auto &s : strs) { + string key = s; + sort(key.begin(), key.end()); + group[key].push_back(s); + } + + vector result; + for (auto it = group.cbegin(); it != group.cend(); it++) { + if (it->second.size() > 1) + result.insert(result.end(), it->second.begin(), it->second.end()); + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{Simplify Path} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:simplify-path} + + +\subsubsection{描述} +Given an absolute path for a file (Unix-style), simplify it. + +For example, \\ +path = \fn{"/home/"}, => \fn{"/home"} \\ +path = \fn{"/a/./b/../../c/"}, => \fn{"/c"} \\ + +Corner Cases: +\begindot +\item Did you consider the case where path = \fn{"/../"}? +In this case, you should return \fn{"/"}. +\item +Another corner case is the path might contain multiple slashes \fn{'/'} together, such as \fn{"/home//foo/"}. +In this case, you should ignore redundant slashes and return \fn{"/home/foo"}. +\myenddot + + +\subsubsection{分析} +很有实际价值的题目。 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Simplify Path +// 时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + string simplifyPath(string const& path) { + vector dirs; // 当做栈 + + for (auto i = path.begin(); i != path.end();) { + ++i; + + auto j = find(i, path.end(), '/'); + auto dir = string(i, j); + + if (!dir.empty() && dir != ".") {// 当有连续 '///'时,dir 为空 + if (dir == "..") { + if (!dirs.empty()) + dirs.pop_back(); + } else + dirs.push_back(dir); + } + + i = j; + } + + stringstream out; + if (dirs.empty()) { + out << "/"; + } else { + for (auto dir : dirs) + out << '/' << dir; + } + + return out.str(); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\section{Length of Last Word} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:length-of-last-word} + + +\subsubsection{描述} +Given a string s consists of upper/lower-case alphabets and empty space characters \fn{' '}, return the length of last word in the string. + +If the last word does not exist, return 0. + +Note: A word is defined as a character sequence consists of non-space characters only. + +For example, +Given \fn{s = "Hello World"}, +return 5. + + +\subsubsection{分析} +细节实现题。 + + +\subsubsection{用 STL} +\begin{Code} +// LeetCode, Length of Last Word +// 偷懒,用 STL +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int lengthOfLastWord(const char *s) { + const string str(s); + auto first = find_if(str.rbegin(), str.rend(), ::isalpha); + auto last = find_if_not(first, str.rend(), ::isalpha); + return distance(first, last); + } +}; +\end{Code} + + +\subsubsection{顺序扫描} +\begin{Code} +// LeetCode, Length of Last Word +// 顺序扫描,记录每个 word 的长度 +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + int lengthOfLastWord(const char *s) { + int len = 0; + while (*s) { + if (*s++ != ' ') + ++len; + else if (*s && *s != ' ') + len = 0; + } + return len; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot diff --git a/leetcode/C++/chapTree.tex b/leetcode/C++/chapTree.tex new file mode 100644 index 00000000..056d8e02 --- /dev/null +++ b/leetcode/C++/chapTree.tex @@ -0,0 +1,2064 @@ +\chapter{树} + +LeetCode 上二叉树的节点定义如下: +\begin{Code} +// 树的节点 +struct TreeNode { + int val; + TreeNode *left; + TreeNode *right; + TreeNode(int x) : val(x), left(nullptr), right(nullptr) { } +}; +\end{Code} + + +\section{二叉树的遍历} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +树的遍历有两类:深度优先遍历和宽度优先遍历。深度优先遍历又可分为两种:先根(次序)遍历和后根(次序)遍历。 + +树的先根遍历是:先访问树的根结点,然后依次先根遍历根的各棵子树。树的先跟遍历的结果与对应二叉树(孩子兄弟表示法)的先序遍历的结果相同。 + +树的后根遍历是:先依次后根遍历树根的各棵子树,然后访问根结点。树的后跟遍历的结果与对应二叉树的中序遍历的结果相同。 + +二叉树的先根遍历有:\textbf{先序遍历}(root->left->right),root->right->left;后根遍历有:\textbf{后序遍历}(left->right->root),right->left->root;二叉树还有个一般的树没有的遍历次序,\textbf{中序遍历}(left->root->right)。 + + +\subsection{Binary Tree Preorder Traversal} +\label{sec:binary-tree-preorder-traversal} + + +\subsubsection{描述} +Given a binary tree, return the \emph{preorder} traversal of its nodes' values. + +For example: +Given binary tree \code{\{1,\#,2,3\}}, +\begin{Code} + 1 + \ + 2 + / + 3 +\end{Code} +return \code{[1,2,3]}. + +Note: Recursive solution is trivial, could you do it iteratively? + + +\subsubsection{分析} +用栈或者Morris遍历。 + + +\subsubsection{栈} +\begin{Code} +// LeetCode, Binary Tree Preorder Traversal +// 使用栈,时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + vector preorderTraversal(TreeNode *root) { + vector result; + const TreeNode *p; + stack s; + + p = root; + if (p != nullptr) s.push(p); + + while (!s.empty()) { + p = s.top(); + s.pop(); + result.push_back(p->val); + + if (p->right != nullptr) s.push(p->right); + if (p->left != nullptr) s.push(p->left); + } + return result; + } +}; +\end{Code} + + +\subsubsection{Morris先序遍历} +\begin{Code} +// LeetCode, Binary Tree Preorder Traversal +// Morris先序遍历,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + vector preorderTraversal(TreeNode *root) { + vector result; + TreeNode *cur, *prev; + + cur = root; + while (cur != nullptr) { + if (cur->left == nullptr) { + result.push_back(cur->val); + prev = cur; /* cur刚刚被访问过 */ + cur = cur->right; + } else { + /* 查找前驱 */ + TreeNode *node = cur->left; + while (node->right != nullptr && node->right != cur) + node = node->right; + + if (node->right == nullptr) { /* 还没线索化,则建立线索 */ + result.push_back(cur->val); /* 仅这一行的位置与中序不同 */ + node->right = cur; + prev = cur; /* cur刚刚被访问过 */ + cur = cur->left; + } else { /* 已经线索化,则删除线索 */ + node->right = nullptr; + /* prev = cur; 不能有这句,cur已经被访问 */ + cur = cur->right; + } + } + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Binary Tree Inorder Traversal,见 \S \ref{sec:binary-tree-inorder-traversal} +\item Binary Tree Postorder Traversal,见 \S \ref{sec:binary-tree-postorder-traversal} +\item Recover Binary Search Tree,见 \S \ref{sec:recover-binary-search-tree} +\myenddot + + +\subsection{Binary Tree Inorder Traversal} +\label{sec:binary-tree-inorder-traversal} + + +\subsubsection{描述} +Given a binary tree, return the \emph{inorder} traversal of its nodes' values. + +For example: +Given binary tree \code{\{1,\#,2,3\}}, +\begin{Code} + 1 + \ + 2 + / + 3 +\end{Code} +return \code{[1,3,2]}. + +Note: Recursive solution is trivial, could you do it iteratively? + + +\subsubsection{分析} +用栈或者Morris遍历。 + + +\subsubsection{栈} +\begin{Code} +// LeetCode, Binary Tree Inorder Traversal +// 使用栈,时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + vector inorderTraversal(TreeNode *root) { + vector result; + const TreeNode *p = root; + stack s; + + while (!s.empty() || p != nullptr) { + if (p != nullptr) { + s.push(p); + p = p->left; + } else { + p = s.top(); + s.pop(); + result.push_back(p->val); + p = p->right; + } + } + return result; + } +}; +\end{Code} + + +\subsubsection{Morris中序遍历} +\begin{Code} +// LeetCode, Binary Tree Inorder Traversal +// Morris中序遍历,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + vector inorderTraversal(TreeNode *root) { + vector result; + TreeNode *cur, *prev; + + cur = root; + while (cur != nullptr) { + if (cur->left == nullptr) { + result.push_back(cur->val); + prev = cur; + cur = cur->right; + } else { + /* 查找前驱 */ + TreeNode *node = cur->left; + while (node->right != nullptr && node->right != cur) + node = node->right; + + if (node->right == nullptr) { /* 还没线索化,则建立线索 */ + node->right = cur; + /* prev = cur; 不能有这句,cur还没有被访问 */ + cur = cur->left; + } else { /* 已经线索化,则访问节点,并删除线索 */ + result.push_back(cur->val); + node->right = nullptr; + prev = cur; + cur = cur->right; + } + } + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Binary Tree Preorder Traversal,见 \S \ref{sec:binary-tree-preorder-traversal} +\item Binary Tree Postorder Traversal,见 \S \ref{sec:binary-tree-postorder-traversal} +\item Recover Binary Search Tree,见 \S \ref{sec:recover-binary-search-tree} +\myenddot + + +\subsection{Binary Tree Postorder Traversal} +\label{sec:binary-tree-postorder-traversal} + + +\subsubsection{描述} +Given a binary tree, return the \emph{postorder} traversal of its nodes' values. + +For example: +Given binary tree \code{\{1,\#,2,3\}}, +\begin{Code} + 1 + \ + 2 + / + 3 +\end{Code} +return \code{[3,2,1]}. + +Note: Recursive solution is trivial, could you do it iteratively? + + +\subsubsection{分析} +用栈或者Morris遍历。 + + +\subsubsection{栈} +\begin{Code} +// LeetCode, Binary Tree Postorder Traversal +// 使用栈,时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + vector postorderTraversal(TreeNode *root) { + vector result; + /* p,正在访问的结点,q,刚刚访问过的结点*/ + const TreeNode *p, *q; + stack s; + + p = root; + + do { + while (p != nullptr) { /* 往左下走*/ + s.push(p); + p = p->left; + } + q = nullptr; + while (!s.empty()) { + p = s.top(); + s.pop(); + /* 右孩子不存在或已被访问,访问之*/ + if (p->right == q) { + result.push_back(p->val); + q = p; /* 保存刚访问过的结点*/ + } else { + /* 当前结点不能访问,需第二次进栈*/ + s.push(p); + /* 先处理右子树*/ + p = p->right; + break; + } + } + } while (!s.empty()); + + return result; + } +}; +\end{Code} + + +\subsubsection{Morris后序遍历} +\begin{Code} +// LeetCode, Binary Tree Postorder Traversal +// Morris后序遍历,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + vector postorderTraversal(TreeNode *root) { + vector result; + TreeNode dummy(-1); + TreeNode *cur, *prev = nullptr; + std::function < void(const TreeNode*)> visit = + [&result](const TreeNode *node){ + result.push_back(node->val); + }; + + dummy.left = root; + cur = &dummy; + while (cur != nullptr) { + if (cur->left == nullptr) { + prev = cur; /* 必须要有 */ + cur = cur->right; + } else { + TreeNode *node = cur->left; + while (node->right != nullptr && node->right != cur) + node = node->right; + + if (node->right == nullptr) { /* 还没线索化,则建立线索 */ + node->right = cur; + prev = cur; /* 必须要有 */ + cur = cur->left; + } else { /* 已经线索化,则访问节点,并删除线索 */ + visit_reverse(cur->left, prev, visit); + prev->right = nullptr; + prev = cur; /* 必须要有 */ + cur = cur->right; + } + } + } + return result; + } +private: + // 逆转路径 + static void reverse(TreeNode *from, TreeNode *to) { + TreeNode *x = from, *y = from->right, *z; + if (from == to) return; + + while (x != to) { + z = y->right; + y->right = x; + x = y; + y = z; + } + } + + // 访问逆转后的路径上的所有结点 + static void visit_reverse(TreeNode* from, TreeNode *to, + std::function< void(const TreeNode*) >& visit) { + TreeNode *p = to; + reverse(from, to); + + while (true) { + visit(p); + if (p == from) + break; + p = p->right; + } + + reverse(to, from); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Binary Tree Preorder Traversal,见 \S \ref{sec:binary-tree-preorder-traversal} +\item Binary Tree Inorder Traversal,见 \S \ref{sec:binary-tree-inorder-traversal} +\item Recover Binary Search Tree,见 \S \ref{sec:recover-binary-search-tree} +\myenddot + + +\subsection{Binary Tree Level Order Traversal} +\label{sec:binary-tree-tevel-order-traversal} + + +\subsubsection{描述} +Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level). + +For example: +Given binary tree \code{\{3,9,20,\#,\#,15,7\}}, +\begin{Code} + 3 + / \ + 9 20 + / \ + 15 7 +\end{Code} +return its level order traversal as: +\begin{Code} +[ + [3], + [9,20], + [15,7] +] +\end{Code} + + +\subsubsection{分析} +无 + + +\subsubsection{递归版} +\begin{Code} +// LeetCode, Binary Tree Level Order Traversal +// 递归版,时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + vector > levelOrder(TreeNode *root) { + vector> result; + traverse(root, 1, result); + return result; + } + + void traverse(TreeNode *root, size_t level, vector> &result) { + if (!root) return; + + if (level > result.size()) + result.push_back(vector()); + + result[level-1].push_back(root->val); + traverse(root->left, level+1, result); + traverse(root->right, level+1, result); + } +}; +\end{Code} + + +\subsubsection{迭代版} +\begin{Code} +// LeetCode, Binary Tree Level Order Traversal +// 迭代版,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + vector > levelOrder(TreeNode *root) { + vector > result; + if(root == nullptr) return result; + + queue current, next; + vector level; // elments in level level + + current.push(root); + while (!current.empty()) { + while (!current.empty()) { + TreeNode* node = current.front(); + current.pop(); + level.push_back(node->val); + if (node->left != nullptr) next.push(node->left); + if (node->right != nullptr) next.push(node->right); + } + result.push_back(level); + level.clear(); + swap(next, current); + } + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Binary Tree Level Order Traversal II,见 \S \ref{sec:binary-tree-tevel-order-traversal-ii} +\item Binary Tree Zigzag Level Order Traversal,见 \S \ref{sec:binary-tree-zigzag-level-order-traversal} +\myenddot + + +\subsection{Binary Tree Level Order Traversal II} +\label{sec:binary-tree-tevel-order-traversal-ii} + + +\subsubsection{描述} +Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, from left to right, level by level from leaf to root). + +For example: +Given binary tree \code{\{3,9,20,\#,\#,15,7\}}, +\begin{Code} + 3 + / \ + 9 20 + / \ + 15 7 +\end{Code} +return its bottom-up level order traversal as: +\begin{Code} +[ + [15,7] + [9,20], + [3], +] +\end{Code} + + +\subsubsection{分析} +在上一题(见\S \ref{sec:binary-tree-tevel-order-traversal})的基础上,\fn{reverse()}一下即可。 + + +\subsubsection{递归版} +\begin{Code} +// LeetCode, Binary Tree Level Order Traversal II +// 递归版,时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + vector > levelOrderBottom(TreeNode *root) { + vector> result; + traverse(root, 1, result); + std::reverse(result.begin(), result.end()); // 比上一题多此一行 + return result; + } + + void traverse(TreeNode *root, size_t level, vector> &result) { + if (!root) return; + + if (level > result.size()) + result.push_back(vector()); + + result[level-1].push_back(root->val); + traverse(root->left, level+1, result); + traverse(root->right, level+1, result); + } +}; +\end{Code} + + +\subsubsection{迭代版} +\begin{Code} +// LeetCode, Binary Tree Level Order Traversal II +// 迭代版,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + vector > levelOrderBottom(TreeNode *root) { + vector > result; + if(root == nullptr) return result; + + queue current, next; + vector level; // elments in level level + + current.push(root); + while (!current.empty()) { + while (!current.empty()) { + TreeNode* node = current.front(); + current.pop(); + level.push_back(node->val); + if (node->left != nullptr) next.push(node->left); + if (node->right != nullptr) next.push(node->right); + } + result.push_back(level); + level.clear(); + swap(next, current); + } + reverse(result.begin(), result.end()); // 比上一题多此一行 + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Binary Tree Level Order Traversal,见 \S \ref{sec:binary-tree-tevel-order-traversal} +\item Binary Tree Zigzag Level Order Traversal,见 \S \ref{sec:binary-tree-zigzag-level-order-traversal} +\myenddot + + +\subsection{Binary Tree Zigzag Level Order Traversal} +\label{sec:binary-tree-zigzag-level-order-traversal} + + +\subsubsection{描述} +Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to right, then right to left for the next level and alternate between). + +For example: +Given binary tree \code{{3,9,20,\#,\#,15,7}}, +\begin{Code} + 3 + / \ + 9 20 + / \ + 15 7 +\end{Code} +return its zigzag level order traversal as: +\begin{Code} +[ + [3], + [20,9], + [15,7] +] +\end{Code} + + +\subsubsection{分析} +广度优先遍历,用一个bool记录是从左到右还是从右到左,每一层结束就翻转一下。 + + +\subsubsection{递归版} +\begin{Code} +// LeetCode, Binary Tree Zigzag Level Order Traversal +// 递归版,时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + vector > zigzagLevelOrder(TreeNode *root) { + vector> result; + traverse(root, 1, result, true); + return result; + } + + void traverse(TreeNode *root, size_t level, vector> &result, + bool left_to_right) { + if (!root) return; + + if (level > result.size()) + result.push_back(vector()); + + if (left_to_right) + result[level-1].push_back(root->val); + else + result[level-1].insert(result[level-1].begin(), root->val); + + traverse(root->left, level+1, result, !left_to_right); + traverse(root->right, level+1, result, !left_to_right); + } +}; +\end{Code} + +\subsubsection{迭代版} +\begin{Code} +//LeetCode, Binary Tree Zigzag Level Order Traversal +//广度优先遍历,用一个bool记录是从左到右还是从右到左,每一层结束就翻转一下。 +// 迭代版,时间复杂度O(n),空间复杂度O(n) +class Solution { +public: + vector > zigzagLevelOrder(TreeNode *root) { + vector > result; + if (nullptr == root) return result; + + queue q; + bool left_to_right = true; //left to right + vector level; // one level's elements + + q.push(root); + q.push(nullptr); // level separator + while (!q.empty()) { + TreeNode *cur = q.front(); + q.pop(); + if (cur) { + level.push_back(cur->val); + if (cur->left) q.push(cur->left); + if (cur->right) q.push(cur->right); + } else { + if (left_to_right) { + result.push_back(level); + } else { + reverse(level.begin(), level.end()); + result.push_back(level); + } + level.clear(); + left_to_right = !left_to_right; + + if (q.size() > 0) q.push(nullptr); + } + } + + return result; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Binary Tree Level Order Traversal,见 \S \ref{sec:binary-tree-tevel-order-traversal} +\item Binary Tree Level Order Traversal II,见 \S \ref{sec:binary-tree-tevel-order-traversal-ii} +\myenddot + + +\subsection{Recover Binary Search Tree} +\label{sec:recover-binary-search-tree} + + +\subsubsection{描述} +Two elements of a binary search tree (BST) are swapped by mistake. + +Recover the tree without changing its structure. + +Note: A solution using $O(n)$ space is pretty straight forward. Could you devise a constant space solution? + + +\subsubsection{分析} +$O(n)$空间的解法是,开一个指针数组,中序遍历,将节点指针依次存放到数组里,然后寻找两处逆向的位置,先从前往后找第一个逆序的位置,然后从后往前找第二个逆序的位置,交换这两个指针的值。 + +中序遍历一般需要用到栈,空间也是$O(n)$的,如何才能不使用栈?Morris中序遍历。 + + +\subsubsection{代码} + +\begin{Code} +// LeetCode, Recover Binary Search Tree +// Morris中序遍历,时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + void recoverTree(TreeNode* root) { + pair broken; + TreeNode* prev = nullptr; + TreeNode* cur = root; + + while (cur != nullptr) { + if (cur->left == nullptr) { + detect(broken, prev, cur); + prev = cur; + cur = cur->right; + } else { + auto node = cur->left; + + while (node->right != nullptr && node->right != cur) + node = node->right; + + if (node->right == nullptr) { + node->right = cur; + //prev = cur; 不能有这句!因为cur还没有被访问 + cur = cur->left; + } else { + detect(broken, prev, cur); + node->right = nullptr; + prev = cur; + cur = cur->right; + } + } + } + + swap(broken.first->val, broken.second->val); + } + + void detect(pair& broken, TreeNode* prev, + TreeNode* current) { + if (prev != nullptr && prev->val > current->val) { + if (broken.first == nullptr) { + broken.first = prev; + } //不能用else,例如 {0,1},会导致最后 swap时second为nullptr, + //会 Runtime Error + broken.second = current; + } + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Binary Tree Inorder Traversal,见 \S \ref{sec:binary-tree-inorder-traversal} +\myenddot + + +\subsection{Same Tree} +\label{sec:same-tree} + + +\subsubsection{描述} +Given two binary trees, write a function to check if they are equal or not. + +Two binary trees are considered equal if they are structurally identical and the nodes have the same value. + + +\subsubsection{分析} +无 + + +\subsubsection{递归版} +递归版 +\begin{Code} +// LeetCode, Same Tree +// 递归版,时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + bool isSameTree(TreeNode *p, TreeNode *q) { + if (!p && !q) return true; // 终止条件 + if (!p || !q) return false; // 剪枝 + return p->val == q->val // 三方合并 + && isSameTree(p->left, q->left) + && isSameTree(p->right, q->right); + } +}; +\end{Code} + + +\subsubsection{迭代版} +\begin{Code} +// LeetCode, Same Tree +// 迭代版,时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + bool isSameTree(TreeNode *p, TreeNode *q) { + stack s; + s.push(p); + s.push(q); + + while(!s.empty()) { + p = s.top(); s.pop(); + q = s.top(); s.pop(); + + if (!p && !q) continue; + if (!p || !q) return false; + if (p->val != q->val) return false; + + s.push(p->left); + s.push(q->left); + + s.push(p->right); + s.push(q->right); + } + return true; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Symmetric Tree,见 \S \ref{sec:symmetric-tree} +\myenddot + + +\subsection{Symmetric Tree} +\label{sec:symmetric-tree} + + +\subsubsection{描述} +Given two binary trees, write a function to check if they are equal or not. + +Two binary trees are considered equal if they are structurally identical and the nodes have the same value. + + +\subsubsection{分析} +无 + + +\subsubsection{递归版} +\begin{Code} +// LeetCode, Symmetric Tree +// 递归版,时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + bool isSymmetric(TreeNode *root) { + return root ? isSymmetric(root->left, root->right) : true; + } + bool isSymmetric(TreeNode *left, TreeNode *right) { + if (!left && !right) return true; // 终止条件 + if (!left || !right) return false; // 终止条件 + return left->val == right->val // 三方合并 + && isSymmetric(left->left, right->right) + && isSymmetric(left->right, right->left); + } +}; +\end{Code} + + +\subsubsection{迭代版} +\begin{Code} +// LeetCode, Symmetric Tree +// 迭代版,时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + bool isSymmetric (TreeNode* root) { + if (!root) return true; + + stack s; + s.push(root->left); + s.push(root->right); + + while (!s.empty ()) { + auto p = s.top (); s.pop(); + auto q = s.top (); s.pop(); + + if (!p && !q) continue; + if (!p || !q) return false; + if (p->val != q->val) return false; + + s.push(p->left); + s.push(q->right); + + s.push(p->right); + s.push(q->left); + } + + return true; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Same Tree,见 \S \ref{sec:same-tree} +\myenddot + + +\subsection{Balanced Binary Tree} +\label{sec:balanced-binary-tree} + + +\subsubsection{描述} +Given a binary tree, determine if it is height-balanced. + +For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1. + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Balanced Binary Tree +// 时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + bool isBalanced (TreeNode* root) { + return balancedHeight (root) >= 0; + } + + /** + * Returns the height of `root` if `root` is a balanced tree, + * otherwise, returns `-1`. + */ + int balancedHeight (TreeNode* root) { + if (root == nullptr) return 0; // 终止条件 + + int left = balancedHeight (root->left); + int right = balancedHeight (root->right); + + if (left < 0 || right < 0 || abs(left - right) > 1) return -1; // 剪枝 + + return max(left, right) + 1; // 三方合并 + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\subsection{Flatten Binary Tree to Linked List} +\label{sec:flatten-binary-tree-to-linked-list} + + +\subsubsection{描述} +Given a binary tree, flatten it to a linked list in-place. + +For example, Given +\begin{Code} + 1 + / \ + 2 5 + / \ \ + 3 4 6 +\end{Code} + +The flattened tree should look like: +\begin{Code} + 1 + \ + 2 + \ + 3 + \ + 4 + \ + 5 + \ + 6 +\end{Code} + +\subsubsection{分析} +无 + + +\subsubsection{递归版1} +\begin{Code} +// LeetCode, Flatten Binary Tree to Linked List +// 递归版1,时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + void flatten(TreeNode *root) { + if (root == nullptr) return; // 终止条件 + + flatten(root->left); + flatten(root->right); + + if (nullptr == root->left) return; + + // 三方合并,将左子树所形成的链表插入到root和root->right之间 + TreeNode *p = root->left; + while(p->right) p = p->right; //寻找左链表最后一个节点 + p->right = root->right; + root->right = root->left; + root->left = nullptr; + } +}; +\end{Code} + + +\subsubsection{递归版2} +\begin{Code} +// LeetCode, Flatten Binary Tree to Linked List +// 递归版2 +// @author 王顺达(http://weibo.com/u/1234984145) +// 时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + void flatten(TreeNode *root) { + flatten(root, NULL); + } +private: + // 把root所代表树变成链表后,tail跟在该链表后面 + TreeNode *flatten(TreeNode *root, TreeNode *tail) { + if (NULL == root) return tail; + + root->right = flatten(root->left, flatten(root->right, tail)); + root->left = NULL; + return root; + } +}; +\end{Code} + + +\subsubsection{迭代版} +\begin{Code} +// LeetCode, Flatten Binary Tree to Linked List +// 迭代版,时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + void flatten(TreeNode* root) { + if (root == nullptr) return; + + stack s; + s.push(root); + + while (!s.empty()) { + auto p = s.top(); + s.pop(); + + if (p->right) + s.push(p->right); + if (p->left) + s.push(p->left); + + p->left = nullptr; + if (!s.empty()) + p->right = s.top(); + } + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot + + +\subsection{Populating Next Right Pointers in Each Node II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:populating-next-right-pointers-in-each-node-ii} + + +\subsubsection{描述} +Follow up for problem "Populating Next Right Pointers in Each Node". + +What if the given tree could be any binary tree? Would your previous solution still work? + +Note: You may only use constant extra space. + +For example, +Given the following binary tree, +\begin{Code} + 1 + / \ + 2 3 + / \ \ + 4 5 7 +\end{Code} + +After calling your function, the tree should look like: +\begin{Code} + 1 -> NULL + / \ + 2 -> 3 -> NULL + / \ \ + 4-> 5 -> 7 -> NULL +\end{Code} + + +\subsubsection{分析} +要处理一个节点,可能需要最右边的兄弟节点,首先想到用广搜。但广搜不是常数空间的,本题要求常数空间。 + +注意,这题的代码原封不动,也可以解决 Populating Next Right Pointers in Each Node I. + + +\subsubsection{递归版} +\begin{Code} +// LeetCode, Populating Next Right Pointers in Each Node II +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + void connect(TreeLinkNode *root) { + if (root == nullptr) return; + + TreeLinkNode dummy(-1); + for (TreeLinkNode *curr = root, *prev = &dummy; + curr; curr = curr->next) { + if (curr->left != nullptr){ + prev->next = curr->left; + prev = prev->next; + } + if (curr->right != nullptr){ + prev->next = curr->right; + prev = prev->next; + } + } + connect(dummy.next); + } +}; +\end{Code} + + +\subsubsection{迭代版} +\begin{Code} +// LeetCode, Populating Next Right Pointers in Each Node II +// 时间复杂度O(n),空间复杂度O(1) +class Solution { +public: + void connect(TreeLinkNode *root) { + while (root) { + TreeLinkNode * next = nullptr; // the first node of next level + TreeLinkNode * prev = nullptr; // previous node on the same level + for (; root; root = root->next) { + if (!next) next = root->left ? root->left : root->right; + + if (root->left) { + if (prev) prev->next = root->left; + prev = root->left; + } + if (root->right) { + if (prev) prev->next = root->right; + prev = root->right; + } + } + root = next; // turn to next level + } + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Populating Next Right Pointers in Each Node,见 \S \ref{sec:populating-next-right-pointers-in-each-node} +\myenddot + + +\section{二叉树的构建} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +\subsection{Construct Binary Tree from Preorder and Inorder Traversal} +\label{sec:construct-binary-tree-from-preorder-and-inorder-traversal} + + +\subsubsection{描述} +Given preorder and inorder traversal of a tree, construct the binary tree. + +Note: +You may assume that duplicates do not exist in the tree. + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Construct Binary Tree from Preorder and Inorder Traversal +// 递归,时间复杂度O(n),空间复杂度O(\logn) +class Solution { +public: + TreeNode* buildTree(vector& preorder, vector& inorder) { + return buildTree(begin(preorder), end(preorder), + begin(inorder), end(inorder)); + } + + template + TreeNode* buildTree(InputIterator pre_first, InputIterator pre_last, + InputIterator in_first, InputIterator in_last) { + if (pre_first == pre_last) return nullptr; + if (in_first == in_last) return nullptr; + + auto root = new TreeNode(*pre_first); + + auto inRootPos = find(in_first, in_last, *pre_first); + auto leftSize = distance(in_first, inRootPos); + + root->left = buildTree(next(pre_first), next(pre_first, + leftSize + 1), in_first, next(in_first, leftSize)); + root->right = buildTree(next(pre_first, leftSize + 1), pre_last, + next(inRootPos), in_last); + + return root; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Construct Binary Tree from Inorder and Postorder Traversal,见 \S \ref{sec:construct-binary-tree-from-inorder-and-postorder-traversal} +\myenddot + + +\subsection{Construct Binary Tree from Inorder and Postorder Traversal} +\label{sec:construct-binary-tree-from-inorder-and-postorder-traversal} + + +\subsubsection{描述} +Given inorder and postorder traversal of a tree, construct the binary tree. + +Note: +You may assume that duplicates do not exist in the tree. + + +\subsubsection{分析} +无 + + +\subsubsection{代码} +\begin{Code} +// LeetCode, Construct Binary Tree from Inorder and Postorder Traversal +// 递归,时间复杂度O(n),空间复杂度O(\logn) +class Solution { +public: + TreeNode* buildTree(vector& inorder, vector& postorder) { + return buildTree(begin(inorder), end(inorder), + begin(postorder), end(postorder)); + } + + template + TreeNode* buildTree(BidiIt in_first, BidiIt in_last, + BidiIt post_first, BidiIt post_last) { + if (in_first ==in_last) return nullptr; + if (post_first == post_last) return nullptr; + + const auto val = *prev(post_last); + TreeNode* root = new TreeNode(val); + + auto in_root_pos = find(in_first, in_last, val); + auto left_size = distance(in_first, in_root_pos); + auto post_left_last = next(post_first, left_size); + + root->left = buildTree(in_first, in_root_pos, post_first, post_left_last); + root->right = buildTree(next(in_root_pos), in_last, post_left_last, + prev(post_last)); + + return root; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Construct Binary Tree from Preorder and Inorder Traversal,见 \S \ref{sec:construct-binary-tree-from-preorder-and-inorder-traversal} +\myenddot + + +\section{二叉查找树} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +\subsection{Unique Binary Search Trees} +\label{sec:unique-binary-search-trees} + + +\subsubsection{描述} +Given $n$, how many structurally unique BST's (binary search trees) that store values $1...n$? + +For example, +Given $n = 3$, there are a total of 5 unique BST's. +\begin{Code} + 1 3 3 2 1 + \ / / / \ \ + 3 2 1 1 3 2 + / / \ \ + 2 1 2 3 +\end{Code} + +\subsubsection{分析} +如果把上例的顺序改一下,就可以看出规律了。 +\begin{Code} + 1 1 2 3 3 + \ \ / \ / / + 3 2 1 3 2 1 + / \ / \ +2 3 1 2 +\end{Code} + +比如,以1为根的树的个数,等于左子树的个数乘以右子树的个数,左子树是0个元素的树,右子树是2个元素的树。以2为根的树的个数,等于左子树的个数乘以右子树的个数,左子树是1个元素的树,右子树也是1个元素的树。依此类推。 + +当数组为 $1,2,3,...,n$时,基于以下原则的构建的BST树具有唯一性: +\textbf{以i为根节点的树,其左子树由[1, i-1]构成, 其右子树由[i+1, n]构成。} + +定义$f(i)$为以$[1,i]$能产生的Unique Binary Search Tree的数目,则 + +如果数组为空,毫无疑问,只有一种BST,即空树,$f(0)=1$。 + +如果数组仅有一个元素{1},只有一种BST,单个节点,$f(1)=1$。 + +如果数组有两个元素{1,2}, 那么有如下两种可能 +\begin{Code} +1 2 + \ / + 2 1 +\end{Code} + +\begin{eqnarray} +f(2) &=& f(0) * f(1) \text{ ,1为根的情况} \nonumber \\ + &+& f(1) * f(0) \text{ ,2为根的情况} \nonumber +\end{eqnarray} + +再看一看3个元素的数组,可以发现BST的取值方式如下: +\begin{eqnarray} +f(3) &=& f(0) * f(2) \text{ ,1为根的情况} \nonumber \\ + &+& f(1) * f(1) \text{ ,2为根的情况} \nonumber \\ + &+& f(2) * f(0) \text{ ,3为根的情况} \nonumber +\end{eqnarray} + +所以,由此观察,可以得出$f$的递推公式为 +$$ +f(i) = \sum_{k=1}^{i} f(k-1) \times f(i-k) +$$ +至此,问题划归为一维动态规划。 + + +\subsubsection{代码} + +\begin{Code} +// LeetCode, Unique Binary Search Trees +// 时间复杂度O(n^2),空间复杂度O(n) +class Solution { +public: + int numTrees(int n) { + vector f(n + 1, 0); + + f[0] = 1; + f[1] = 1; + for (int i = 2; i <= n; ++i) { + for (int k = 1; k <= i; ++k) + f[i] += f[k-1] * f[i - k]; + } + + return f[n]; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Unique Binary Search Trees II,见 \S \ref{sec:unique-binary-search-trees-ii} +\myenddot + + +\subsection{Unique Binary Search Trees II} +\label{sec:unique-binary-search-trees-ii} + + +\subsubsection{描述} +Given $n$, generate all structurally unique BST's (binary search trees) that store values 1...n. + +For example, +Given $n = 3$, your program should return all 5 unique BST's shown below. +\begin{Code} + 1 3 3 2 1 + \ / / / \ \ + 3 2 1 1 3 2 + / / \ \ + 2 1 2 3 +\end{Code} + + +\subsubsection{分析} +见前面一题。 + + +\subsubsection{代码} + +\begin{Code} +// LeetCode, Unique Binary Search Trees II +// 时间复杂度TODO,空间复杂度TODO +class Solution { +public: + vector generateTrees(int n) { + if (n == 0) return generate(1, 0); + return generate(1, n); + } +private: + vector generate(int start, int end) { + vector subTree; + if (start > end) { + subTree.push_back(nullptr); + return subTree; + } + for (int k = start; k <= end; k++) { + vector leftSubs = generate(start, k - 1); + vector rightSubs = generate(k + 1, end); + for (auto i : leftSubs) { + for (auto j : rightSubs) { + TreeNode *node = new TreeNode(k); + node->left = i; + node->right = j; + subTree.push_back(node); + } + } + } + return subTree; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Unique Binary Search Trees,见 \S \ref{sec:unique-binary-search-trees} +\myenddot + + +\subsection{Validate Binary Search Tree} +\label{sec:validate-binary-search-tree} + + +\subsubsection{描述} +Given a binary tree, determine if it is a valid binary search tree (BST). + +Assume a BST is defined as follows: +\begindot +\item The left subtree of a node contains only nodes with keys less than the node's key. +\item The right subtree of a node contains only nodes with keys greater than the node's key. +\item Both the left and right subtrees must also be binary search trees. +\myenddot + + +\subsubsection{分析} + + +\subsubsection{代码} + +\begin{Code} +// LeetCode, Validate Binary Search Tree +// 时间复杂度O(n),空间复杂度O(\logn) +class Solution { +public: + bool isValidBST(TreeNode* root) { + return isValidBST(root, INT_MIN, INT_MAX); + } + + bool isValidBST(TreeNode* root, int lower, int upper) { + if (root == nullptr) return true; + + return root->val > lower && root->val < upper + && isValidBST(root->left, lower, root->val) + && isValidBST(root->right, root->val, upper); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Validate Binary Search Tree,见 \S \ref{sec:validate-binary-search-tree} +\myenddot + + +\subsection{Convert Sorted Array to Binary Search Tree} +\label{sec:convert-sorted-array-to-binary-search-tree} + + +\subsubsection{描述} +Given an array where elements are sorted in ascending order, convert it to a height balanced BST. + + +\subsubsection{分析} +二分法。 + + +\subsubsection{代码} + +\begin{Code} +// LeetCode, Convert Sorted Array to Binary Search Tree +// 分治法,时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + TreeNode* sortedArrayToBST (vector& num) { + return sortedArrayToBST(num.begin(), num.end()); + } + + template + TreeNode* sortedArrayToBST (RandomAccessIterator first, + RandomAccessIterator last) { + const auto length = distance(first, last); + + if (length <= 0) return nullptr; // 终止条件 + + // 三方合并 + auto mid = first + length / 2; + TreeNode* root = new TreeNode (*mid); + root->left = sortedArrayToBST(first, mid); + root->right = sortedArrayToBST(mid + 1, last); + + return root; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Convert Sorted List to Binary Search Tree,见 \S \ref{sec:convert-sorted-list-to-binary-search-tree} +\myenddot + + +\subsection{Convert Sorted List to Binary Search Tree} +\label{sec:convert-sorted-list-to-binary-search-tree} + + +\subsubsection{描述} +Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST. + + +\subsubsection{分析} +这题与上一题类似,但是单链表不能随机访问,而自顶向下的二分法必须需要RandomAccessIterator,因此前面的方法不适用本题。 + +存在一种自底向上(bottom-up)的方法,见\myurl{http://leetcode.com/2010/11/convert-sorted-list-to-balanced-binary.html} + + +\subsubsection{分治法,自顶向下} +分治法,类似于 Convert Sorted Array to Binary Search Tree,自顶向下,复杂度$O(n\log n)$。 +\begin{Code} +// LeetCode, Convert Sorted List to Binary Search Tree +// 分治法,类似于 Convert Sorted Array to Binary Search Tree, +// 自顶向下,时间复杂度O(n^2),空间复杂度O(logn) +class Solution { +public: + TreeNode* sortedListToBST (ListNode* head) { + return sortedListToBST (head, listLength (head)); + } + + TreeNode* sortedListToBST (ListNode* head, int len) { + if (len == 0) return nullptr; + if (len == 1) return new TreeNode (head->val); + + TreeNode* root = new TreeNode (nth_node (head, len / 2 + 1)->val); + root->left = sortedListToBST (head, len / 2); + root->right = sortedListToBST (nth_node (head, len / 2 + 2), + (len - 1) / 2); + + return root; + } + + int listLength (ListNode* node) { + int n = 0; + + while(node) { + ++n; + node = node->next; + } + + return n; + } + + ListNode* nth_node (ListNode* node, int n) { + while (--n) + node = node->next; + + return node; + } +}; +\end{Code} + + +\subsubsection{自底向上} +\begin{Code} +// LeetCode, Convert Sorted List to Binary Search Tree +// bottom-up,时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + TreeNode *sortedListToBST(ListNode *head) { + int len = 0; + ListNode *p = head; + while (p) { + len++; + p = p->next; + } + return sortedListToBST(head, 0, len - 1); + } +private: + TreeNode* sortedListToBST(ListNode*& list, int start, int end) { + if (start > end) return nullptr; + + int mid = start + (end - start) / 2; + TreeNode *leftChild = sortedListToBST(list, start, mid - 1); + TreeNode *parent = new TreeNode(list->val); + parent->left = leftChild; + list = list->next; + parent->right = sortedListToBST(list, mid + 1, end); + return parent; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Convert Sorted Array to Binary Search Tree,见 \S \ref{sec:convert-sorted-array-to-binary-search-tree} +\myenddot + + +\section{二叉树的递归} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +二叉树是一个递归的数据结构,因此是一个用来考察递归思维能力的绝佳数据结构。 + +递归一定是深搜(见 \S \ref{sec:dfs-vs-recursion}节 “深搜与递归的区别”),由于在二叉树上,递归的味道更浓些,因此本节用“二叉树的递归”作为标题,而不是“二叉树的深搜”,尽管本节所有的算法都属于深搜。 + +二叉树的先序、中序、后序遍历都可以看做是DFS,此外还有其他顺序的深度优先遍历,共有$3!=6$种。其他3种顺序是 \fn{root->r->l,r->root->l, r->l->root}。 + + +\subsection{Minimum Depth of Binary Tree} +\label{sec:minimum-depth-of-binary-tree} + + +\subsubsection{描述} +Given a binary tree, find its minimum depth. + +The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node. + + +\subsubsection{分析} +无 + + +\subsubsection{递归版} +\begin{Code} +// LeetCode, Minimum Depth of Binary Tree +// 递归版,时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + int minDepth(const TreeNode *root) { + return minDepth(root, false); + } +private: + static int minDepth(const TreeNode *root, bool hasbrother) { + if (!root) return hasbrother ? INT_MAX : 0; + + return 1 + min(minDepth(root->left, root->right != NULL), + minDepth(root->right, root->left != NULL)); + } +}; +\end{Code} + + +\subsubsection{迭代版} +\begin{Code} +// LeetCode, Minimum Depth of Binary Tree +// 迭代版,时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + int minDepth(TreeNode* root) { + if (root == nullptr) + return 0; + + int result = INT_MAX; + + stack> s; + s.push(make_pair(root, 1)); + + while (!s.empty()) { + auto node = s.top().first; + auto depth = s.top().second; + s.pop(); + + if (node->left == nullptr && node->right == nullptr) + result = min(result, depth); + + if (node->left && result > depth) // 深度控制,剪枝 + s.push(make_pair(node->left, depth + 1)); + + if (node->right && result > depth) // 深度控制,剪枝 + s.push(make_pair(node->right, depth + 1)); + } + + return result; + } +}; +\end{Code} + +\subsubsection{相关题目} +\begindot +\item Maximum Depth of Binary Tree,见 \S \ref{sec:maximum-depth-of-binary-tree} +\myenddot + + +\subsection{Maximum Depth of Binary Tree} +\label{sec:maximum-depth-of-binary-tree} + + +\subsubsection{描述} +Given a binary tree, find its maximum depth. + +The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node. + + +\subsubsection{分析} +无 + +\subsubsection{代码} + +\begin{Code} +// LeetCode, Maximum Depth of Binary Tree +// 时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + int maxDepth(TreeNode *root) { + if (root == nullptr) return 0; + + return max(maxDepth(root->left), maxDepth(root->right)) + 1; + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Minimum Depth of Binary Tree,见 \S \ref{sec:minimum-depth-of-binary-tree} +\myenddot + + +\subsection{Path Sum} +\label{sec:path-sum} + + +\subsubsection{描述} +Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum. + +For example: +Given the below binary tree and \code{sum = 22}, +\begin{Code} + 5 + / \ + 4 8 + / / \ + 11 13 4 + / \ \ + 7 2 1 +\end{Code} +return true, as there exist a root-to-leaf path \code{5->4->11->2} which sum is 22. + + +\subsubsection{分析} +题目只要求返回\fn{true}或者\fn{false},因此不需要记录路径。 + +由于只需要求出一个结果,因此,当左、右任意一棵子树求到了满意结果,都可以及时return。 + +由于题目没有说节点的数据一定是正整数,必须要走到叶子节点才能判断,因此中途没法剪枝,只能进行朴素深搜。 + +\subsubsection{代码} + +\begin{Code} +// LeetCode, Path Sum +// 时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + bool hasPathSum(TreeNode *root, int sum) { + if (root == nullptr) return false; + + if (root->left == nullptr && root->right == nullptr) // leaf + return sum == root->val; + + return hasPathSum(root->left, sum - root->val) + || hasPathSum(root->right, sum - root->val); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Path Sum II,见 \S \ref{sec:path-sum-ii} +\myenddot + + +\subsection{Path Sum II} +\label{sec:path-sum-ii} + + +\subsubsection{描述} +Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum. + +For example: +Given the below binary tree and \code{sum = 22}, +\begin{Code} + 5 + / \ + 4 8 + / / \ + 11 13 4 + / \ / \ + 7 2 5 1 +\end{Code} +return +\begin{Code} +[ + [5,4,11,2], + [5,8,4,5] +] +\end{Code} + + +\subsubsection{分析} +跟上一题相比,本题是求路径本身。且要求出所有结果,左子树求到了满意结果,不能return,要接着求右子树。 + +\subsubsection{代码} + +\begin{Code} +// LeetCode, Path Sum II +// 时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + vector > pathSum(TreeNode *root, int sum) { + vector > result; + vector cur; // 中间结果 + pathSum(root, sum, cur, result); + return result; + } +private: + void pathSum(TreeNode *root, int gap, vector &cur, + vector > &result) { + if (root == nullptr) return; + + cur.push_back(root->val); + + if (root->left == nullptr && root->right == nullptr) { // leaf + if (gap == root->val) + result.push_back(cur); + } + pathSum(root->left, gap - root->val, cur, result); + pathSum(root->right, gap - root->val, cur, result); + + cur.pop_back(); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Path Sum,见 \S \ref{sec:path-sum} +\myenddot + + +\subsection{Binary Tree Maximum Path Sum} +\label{sec:binary-tree-maximum-path-sum} + + +\subsubsection{描述} +Given a binary tree, find the maximum path sum. + +The path may start and end at any node in the tree. +For example: +Given the below binary tree, +\begin{Code} + 1 + / \ +2 3 +\end{Code} +Return $6$. + + +\subsubsection{分析} +这题很难,路径可以从任意节点开始,到任意节点结束。 + +可以利用“最大连续子序列和”问题的思路,见第\S \ref{sec:maximum-subarray}节。如果说Array只有一个方向的话,那么Binary Tree其实只是左、右两个方向而已,我们需要比较两个方向上的值。 + +不过,Array可以从头到尾遍历,那么Binary Tree怎么办呢,我们可以采用Binary Tree最常用的dfs来进行遍历。先算出左右子树的结果L和R,如果L大于0,那么对后续结果是有利的,我们加上L,如果R大于0,对后续结果也是有利的,继续加上R。 + +\subsubsection{代码} + +\begin{Code} +// LeetCode, Binary Tree Maximum Path Sum +// 时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + int maxPathSum(TreeNode *root) { + max_sum = INT_MIN; + dfs(root); + return max_sum; + } +private: + int max_sum; + int dfs(const TreeNode *root) { + if (root == nullptr) return 0; + int l = dfs(root->left); + int r = dfs(root->right); + int sum = root->val; + if (l > 0) sum += l; + if (r > 0) sum += r; + max_sum = max(max_sum, sum); + return max(r, l) > 0 ? max(r, l) + root->val : root->val; + } +}; +\end{Code} + +注意,最后return的时候,只返回一个方向上的值,为什么?这是因为在递归中,只能向父节点返回,不可能存在L->root->R的路径,只可能是L->root或R->root。 + + +\subsubsection{相关题目} +\begindot +\item Maximum Subarray,见 \S \ref{sec:maximum-subarray} +\myenddot + + +\subsection{Populating Next Right Pointers in Each Node} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:populating-next-right-pointers-in-each-node} + + +\subsubsection{描述} +Given a binary tree +\begin{Code} +struct TreeLinkNode { + int val; + TreeLinkNode *left, *right, *next; + TreeLinkNode(int x) : val(x), left(NULL), right(NULL), next(NULL) {} +}; +\end{Code} + +Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to \fn{NULL}. + +Initially, all next pointers are set to \fn{NULL}. + +Note: +\begindot +\item You may only use constant extra space. +\item You may assume that it is a perfect binary tree (ie, all leaves are at the same level, and every parent has two children). +\myenddot + +For example, +Given the following perfect binary tree, +\begin{Code} + 1 + / \ + 2 3 + / \ / \ + 4 5 6 7 +\end{Code} + +After calling your function, the tree should look like: +\begin{Code} + 1 -> NULL + / \ + 2 -> 3 -> NULL + / \ / \ + 4->5->6->7 -> NULL +\end{Code} + + +\subsubsection{分析} +无 + +\subsubsection{代码} + +\begin{Code} +// LeetCode, Populating Next Right Pointers in Each Node +// 时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + void connect(TreeLinkNode *root) { + connect(root, NULL); + } +private: + void connect(TreeLinkNode *root, TreeLinkNode *sibling) { + if (root == nullptr) + return; + else + root->next = sibling; + + connect(root->left, root->right); + if (sibling) + connect(root->right, sibling->left); + else + connect(root->right, nullptr); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item Populating Next Right Pointers in Each Node II,见 \S \ref{sec:populating-next-right-pointers-in-each-node-ii} +\myenddot + + +\subsection{Sum Root to Leaf Numbers} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:sum-root-to-leaf-numbers} + + +\subsubsection{描述} +Given a binary tree containing digits from \fn{0-9} only, each root-to-leaf path could represent a number. + +An example is the root-to-leaf path \fn{1->2->3} which represents the number \fn{123}. + +Find the total sum of all root-to-leaf numbers. + +For example, +\begin{Code} + 1 + / \ + 2 3 +\end{Code} + +The root-to-leaf path \fn{1->2} represents the number \fn{12}. +The root-to-leaf path \fn{1->3} represents the number \fn{13}. + +Return the sum = \fn{12 + 13 = 25}. + + +\subsubsection{分析} +无 + +\subsubsection{代码} + +\begin{Code} +// LeetCode, Decode Ways +// 时间复杂度O(n),空间复杂度O(logn) +class Solution { +public: + int sumNumbers(TreeNode *root) { + return dfs(root, 0); + } +private: + int dfs(TreeNode *root, int sum) { + if (root == nullptr) return 0; + if (root->left == nullptr && root->right == nullptr) + return sum * 10 + root->val; + + return dfs(root->left, sum * 10 + root->val) + + dfs(root->right, sum * 10 + root->val); + } +}; +\end{Code} + + +\subsubsection{相关题目} +\begindot +\item 无 +\myenddot diff --git a/leetcode/C++/chapTrick.tex b/leetcode/C++/chapTrick.tex new file mode 100644 index 00000000..de99bbd0 --- /dev/null +++ b/leetcode/C++/chapTrick.tex @@ -0,0 +1,28 @@ +\chapter{编程技巧} + +在判断两个浮点数a和b是否相等时,不要用\fn{a==b},应该判断二者之差的绝对值\fn{fabs(a-b)}是否小于某个阈值,例如\fn{1e-9}。 + +判断一个整数是否是为奇数,用\fn{x \% 2 != 0},不要用\fn{x \% 2 == 1},因为x可能是负数。 + +用\fn{char}的值作为数组下标(例如,统计字符串中每个字符出现的次数),要考虑到\fn{char}可能是负数。有的人考虑到了,先强制转型为\fn{unsigned int}再用作下标,这仍然是错的。正确的做法是,先强制转型为\fn{unsigned char},再用作下标。这涉及C++整型提升的规则,就不详述了。 + +以下是关于STL使用技巧的,很多条款来自《Effective STL》这本书。 + +\subsubsection{vector和string优先于动态分配的数组} + +首先,在性能上,由于\fn{vector}能够保证连续内存,因此一旦分配了后,它的性能跟原始数组相当; + +其次,如果用new,意味着你要确保后面进行了delete,一旦忘记了,就会出现BUG,且这样需要都写一行delete,代码不够短; + +再次,声明多维数组的话,只能一个一个new,例如: +\begin{Code} +int** ary = new int*[row_num]; +for(int i = 0; i < row_num; ++i) + ary[i] = new int[col_num]; +\end{Code} +用vector的话一行代码搞定, +\begin{Code} +vector > ary(row_num, vector(col_num, 0)); +\end{Code} + +\subsubsection{使用reserve来避免不必要的重新分配} diff --git a/leetcode/C++/format.cls b/leetcode/C++/format.cls new file mode 100644 index 00000000..62a6323e --- /dev/null +++ b/leetcode/C++/format.cls @@ -0,0 +1,101 @@ +\usepackage[centering,paperwidth=180mm,paperheight=230mm,% +body={390pt,530pt},marginparsep=10pt,marginpar=50pt]{geometry} +\usepackage{color} +\usepackage{enumitem} +\usepackage{fancyvrb} +\usepackage[bottom,perpage,symbol*]{footmisc} +\usepackage{graphicx} +\usepackage[hidelinks]{hyperref} +\usepackage{makeidx} +\usepackage[toc]{multitoc} +\usepackage{pifont} +\usepackage{underscore} +\usepackage{amsmath} + +\DefineFNsymbols*{chinese}{{\ding{172}}{\ding{173}}{\ding{174}}{\ding{175}}% +{\ding{176}}{\ding{177}}{\ding{178}}{\ding{179}}{\ding{180}}{\ding{181}}} +\setfnsymbol{chinese} + +\hypersetup{bookmarksnumbered=true,bookmarksdepth=2} + +\CTEXsetup[number={\thechapter}]{chapter} +\CTEXsetup[format+={\raggedleft}]{chapter} +\CTEXsetup[beforeskip={10pt}]{chapter} +\CTEXsetup[afterskip={30pt}]{chapter} +\def\CTEX@chapter@aftername{\par} % \CTEXsetup[aftername={\par}]{chapter} +\CTEXsetup[format+={\raggedright}]{section} +\CTEXsetup[beforeskip={-3.0ex plus -1ex minus -.2ex}]{section} +\CTEXsetup[afterskip={2.3ex plus .2ex minus 0.2ex}]{section} + +\renewcommand \thefigure{\thechapter-\arabic{figure}} +\renewcommand \thetable{\thechapter-\arabic{table}} + +\newcommand\figcaption[1]{\def\@captype{figure}\caption{#1}} +\newcommand\tabcaption[1]{\def\@captype{table}\caption{#1}} + +\long\def\@caption#1[#2]#3{% + \addcontentsline{\csname ext@#1\endcsname}{#1}% + {\protect\numberline{\csname fnum@#1\endcsname}{ \ignorespaces #2}}% change "the" to "fnum@" + \normalsize + \@makecaption{\csname fnum@#1\endcsname}{\ignorespaces #3}} + +\long\def\@makecaption#1#2{% + \vskip\abovecaptionskip + \sbox\@tempboxa{#1\quad#2}% + \ifdim \wd\@tempboxa >\hsize + #1\quad#2\par + \else + \global \@minipagefalse + \hb@xt@\hsize{\hfil\box\@tempboxa\hfil}% + \fi + \vskip\belowcaptionskip} + +\setlength\abovecaptionskip{0pt} + +\setmainfont{Times New Roman} +%\setmainfont{Linux Libertine} +%\setmainfont{TeX Gyre Pagella} +\newfontfamily\urlfont{PT Sans Narrow} +%\setmonofont[AutoFakeBold=1.6,AutoFakeSlant=0.17,Mapping=tex-text-tt]{Inconsolata} +\setCJKfamilyfont{zhyou}{YouYuan} + +\newcommand{\fn}[1]{\texttt{#1}} +\newcommand{\sfn}[1]{\texttt{\small #1}} +\newcommand{\kw}[1]{\textsf{#1}} +\newcommand{\myurl}[1]{{\urlfont #1}} +\newcommand{\mpar}[1]{\marginpar[\hfill\kaishu #1]{\kaishu #1}} +\newcommand{\mn}[1]{\texttt{\bs #1}} +\renewcommand{\today}{\the\year-\the\month-\the\day} +\newcommand\bs{\textbackslash} +\newcommand{\code}[1]{\small{\fontspec{Latin Modern Mono} #1}} + +\newcommand\begindot{\begin{itemize} +[itemsep=2pt plus 2pt minus 2pt,% +topsep=3pt plus 2pt minus 2pt,% +parsep=0pt plus 2pt minus 2pt]} +\newcommand\myenddot{\end{itemize}} + +\newcommand\beginnum{\begin{enumerate} +[itemsep=2pt plus 2pt minus 2pt,% +topsep=3pt plus 2pt minus 2pt,% +parsep=0pt plus 2pt minus 2pt]} +\newcommand\myendnum{\end{enumerate}} + +\DefineVerbatimEnvironment% + {Code}{Verbatim} + {fontsize=\small,baselinestretch=0.9,xleftmargin=3mm} + +\raggedbottom +%\setlength{\parskip}{1ex plus .5ex minus .5ex} + +\input{verbatim.cls} +\DefineVerbatimEnvironment% + {Codex}{Verbatim} + {fontsize=\small,baselinestretch=0.9,xleftmargin=3mm,% + frame=lines,labelposition=all,framesep=5pt} + +\DefineVerbatimEnvironment% + {Code}{Verbatim} + {fontsize=\small,baselinestretch=0.9,xleftmargin=3mm} + +\makeindex diff --git a/leetcode/C++/images/8-queens.png b/leetcode/C++/images/8-queens.png new file mode 100644 index 00000000..4745a3c7 Binary files /dev/null and b/leetcode/C++/images/8-queens.png differ diff --git a/leetcode/C++/images/gray-code-construction.png b/leetcode/C++/images/gray-code-construction.png new file mode 100644 index 00000000..c431fbe1 Binary files /dev/null and b/leetcode/C++/images/gray-code-construction.png differ diff --git a/leetcode/C++/images/histogram-area.png b/leetcode/C++/images/histogram-area.png new file mode 100644 index 00000000..7a1a0ca9 Binary files /dev/null and b/leetcode/C++/images/histogram-area.png differ diff --git a/leetcode/C++/images/histogram.png b/leetcode/C++/images/histogram.png new file mode 100644 index 00000000..43600013 Binary files /dev/null and b/leetcode/C++/images/histogram.png differ diff --git a/leetcode/C++/images/longest-substring-without-repeating-characters.png b/leetcode/C++/images/longest-substring-without-repeating-characters.png new file mode 100644 index 00000000..5211dd86 Binary files /dev/null and b/leetcode/C++/images/longest-substring-without-repeating-characters.png differ diff --git a/leetcode/C++/images/next-permutation.png b/leetcode/C++/images/next-permutation.png new file mode 100644 index 00000000..cc9b729e Binary files /dev/null and b/leetcode/C++/images/next-permutation.png differ diff --git a/leetcode/C++/images/phone-keyboard.png b/leetcode/C++/images/phone-keyboard.png new file mode 100644 index 00000000..f740b9f2 Binary files /dev/null and b/leetcode/C++/images/phone-keyboard.png differ diff --git a/leetcode/C++/images/robot-maze.png b/leetcode/C++/images/robot-maze.png new file mode 100644 index 00000000..4ea8b00d Binary files /dev/null and b/leetcode/C++/images/robot-maze.png differ diff --git a/leetcode/C++/images/rotate-image.png b/leetcode/C++/images/rotate-image.png new file mode 100644 index 00000000..dc0f9633 Binary files /dev/null and b/leetcode/C++/images/rotate-image.png differ diff --git a/leetcode/C++/images/sudoku-solution.png b/leetcode/C++/images/sudoku-solution.png new file mode 100644 index 00000000..9ac1fac1 Binary files /dev/null and b/leetcode/C++/images/sudoku-solution.png differ diff --git a/leetcode/C++/images/sudoku.png b/leetcode/C++/images/sudoku.png new file mode 100644 index 00000000..e3792592 Binary files /dev/null and b/leetcode/C++/images/sudoku.png differ diff --git a/leetcode/C++/images/trapping-rain-water.png b/leetcode/C++/images/trapping-rain-water.png new file mode 100644 index 00000000..578e81eb Binary files /dev/null and b/leetcode/C++/images/trapping-rain-water.png differ diff --git a/leetcode/C++/leetcode-cpp.pdf b/leetcode/C++/leetcode-cpp.pdf new file mode 100644 index 00000000..6e3a2430 Binary files /dev/null and b/leetcode/C++/leetcode-cpp.pdf differ diff --git a/leetcode/C++/leetcode-cpp.tex b/leetcode/C++/leetcode-cpp.tex new file mode 100644 index 00000000..903a4018 --- /dev/null +++ b/leetcode/C++/leetcode-cpp.tex @@ -0,0 +1,57 @@ +\documentclass[10pt,adobefonts,fancyhdr,hyperref,UTF8]{ctexbook} + +\usepackage{multirow} +% for \soul 删除线 +\usepackage{ulem} +% 表头斜线 +\usepackage{diagbox} + +\makeatletter +\input{format.cls} +\makeatother + +\begin{document} +\sloppy +\newcommand\BookTitle{LeetCode题解} +\pagestyle{fancy} +\fancyhf{} +\fancyhead[RE]{\normalfont\small\rmfamily\nouppercase{\leftmark}} +\fancyhead[LO]{\normalfont\small\rmfamily\nouppercase{\rightmark}} +\fancyhead[LE,RO]{\thepage} +%\fancyfoot[LE,LO]{\small\normalfont\youyuan\BookTitle} +%\fancyfoot[RE,RO]{\textsf{\small \color{blue} https://github.com/soulmachine/leetcode}} + +\makeatletter +\@openrightfalse +\makeatother + +\frontmatter % 开始前言目录,页码用罗马数字 + +\include{title} + +\tableofcontents + +\mainmatter % 开始正文,页码用阿拉伯数字 + +\graphicspath{{images/}} + +\include{chapTrick} +\include{chapLinearList} +\include{chapString} +\include{chapStackAndQueue} +\include{chapTree} +\include{chapSorting} +\include{chapSearching} +\include{chapBruteforce} +\include{chapBFS} +\include{chapDFS} +\include{chapDivideAndConquer} +\include{chapGreedy} +\include{chapDynamicProgramming} +\include{chapGraph} +\include{chapImplement} + +\appendix % 开始附录,章用字母编号 +\printindex + +\end{document} diff --git a/leetcode/C++/tex-text-tt.map b/leetcode/C++/tex-text-tt.map new file mode 100644 index 00000000..14277489 --- /dev/null +++ b/leetcode/C++/tex-text-tt.map @@ -0,0 +1,28 @@ +; TECkit mapping for TeX input conventions <-> Unicode characters + +LHSName "TeX-text" +RHSName "UNICODE" + +pass(Unicode) + +; ligatures from Knuth's original CMR fonts +;U+002D U+002D <> U+2013 ; -- -> en dash +;U+002D U+002D U+002D <> U+2014 ; --- -> em dash + +;U+0027 <> U+2019 ; ' -> right single quote +;U+0027 U+0027 <> U+201D ; '' -> right double quote +;U+0022 > U+201D ; " -> right double quote + +;U+0060 <> U+2018 ; ` -> left single quote +;U+0060 U+0060 <> U+201C ; `` -> left double quote + +;U+0021 U+0060 <> U+00A1 ; !` -> inverted exclam +;U+003F U+0060 <> U+00BF ; ?` -> inverted question + +; additions supported in T1 encoding +;U+002C U+002C <> U+201E ; ,, -> DOUBLE LOW-9 QUOTATION MARK +;U+003C U+003C <> U+00AB ; << -> LEFT POINTING GUILLEMET +;U+003E U+003E <> U+00BB ; >> -> RIGHT POINTING GUILLEMET + +;U+0027 <> U+2019 ; ' -> right single quote +;U+0022 > U+201D ; " -> right double quote diff --git a/leetcode/C++/tex-text-tt.tec b/leetcode/C++/tex-text-tt.tec new file mode 100644 index 00000000..c5e48ff0 Binary files /dev/null and b/leetcode/C++/tex-text-tt.tec differ diff --git a/leetcode/C++/title.tex b/leetcode/C++/title.tex new file mode 100644 index 00000000..57d01956 --- /dev/null +++ b/leetcode/C++/title.tex @@ -0,0 +1,22 @@ +\thispagestyle{plain} +\begin{center} + {\LARGE\textbf{\BookTitle}} + + \vspace{1em} + {\large 戴方勤 (soulmachine@gmail.com)} + + \vspace{1ex} + \myurl{https://github.com/soulmachine/leetcode} + + \vspace{1ex} + 最后更新 \today + + \vspace{1em} + \textbf{\large 版权声明} +\end{center} +\noindent 本作品采用“Creative Commons 署名-非商业性使用-相同方式共享 3.0 Unported许可协议 +(cc by-nc-sa)”进行许可。 +\texttt{\small http://creativecommons.org/licenses/by-nc-sa/3.0/} + +\vspace{1em} +\input{abstract} \ No newline at end of file diff --git a/leetcode/C++/verbatim.cls b/leetcode/C++/verbatim.cls new file mode 100644 index 00000000..fa9ce7a6 --- /dev/null +++ b/leetcode/C++/verbatim.cls @@ -0,0 +1,112 @@ +\def\FV@SetLineWidth{% + \if@FV@ResetMargins\else + \advance\leftmargin\@totalleftmargin + \fi + \advance\leftmargin\FV@XLeftMargin\relax + \advance\rightmargin\FV@XRightMargin\relax + \linewidth\hsize + %\advance\linewidth-\leftmargin + %\advance\linewidth-\rightmargin + \hfuzz\FancyVerbHFuzz\relax} + + +\def\FV@SingleFrameLine#1{% +%% DG/SR modification end + \hbox to\z@{% + %\kern\leftmargin +%% DG/SR modification begin - Jun. 22, 1998 + \ifnum#1=\z@ + \let\FV@Label\FV@LabelBegin + \else + \let\FV@Label\FV@LabelEnd + \fi + \ifx\FV@Label\relax +%% DG/SR modification end + \FancyVerbRuleColor{\vrule \@width\linewidth \@height\FV@FrameRule}% +%% DG/SR modification begin - Jun. 22, 1998 + \else + \ifnum#1=\z@ + \setbox\z@\hbox{\strut\enspace\urlfont\FV@LabelBegin\strut}% + \else + \setbox\z@\hbox{\strut\enspace\urlfont\FV@LabelEnd\strut}% + \fi + \@tempdimb=\dp\z@ + \advance\@tempdimb -.5\ht\z@ + \@tempdimc=\linewidth + \advance\@tempdimc -\wd\z@ + %\divide\@tempdimc\tw@ + \ifnum#1=\z@ % Top line + \ifx\FV@LabelPositionTopLine\relax + \FancyVerbRuleColor{\vrule \@width\linewidth \@height\FV@FrameRule}% + \else + \FV@FrameLineWithLabel + \fi + \else % Bottom line + \ifx\FV@LabelPositionBottomLine\relax + \FancyVerbRuleColor{\vrule \@width\linewidth \@height\FV@FrameRule}% + \else + \FV@FrameLineWithLabel + \fi + \fi + \fi +%% DG/SR modification end + \hss}} + + +%% DG/SR modification begin - May. 19, 1998 +\def\FV@FrameLineWithLabel{% + \ht\z@\@tempdimb\dp\z@\@tempdimb% + \FancyVerbRuleColor{% + \raise 0.5ex\hbox{\vrule \@width\@tempdimc \@height\FV@FrameRule}% + \raise\@tempdimb\box\z@}} +%% DG/SR modification end + + +\def\FV@EndListFrame@Lines{% + \begingroup + %\vskip 0.5ex + \baselineskip\z@skip + \kern\FV@FrameSep\relax +%% DG/SR modification begin - May. 19, 1998 +%% \FV@SingleFrameLine + \FV@SingleFrameLine{\@ne}% +%% DG/SR modification end + \endgroup} + +\newskip\mytopsep +\setlength{\mytopsep}{4pt plus 2pt minus 3pt} + +\def\FV@ListVSpace{% + \@topsepadd\mytopsep + \if@noparlist\advance\@topsepadd\partopsep\fi + \if@inlabel + \vskip\parskip + \else + \if@nobreak + \vskip\parskip + \clubpenalty\@M + \else + \addpenalty\@beginparpenalty + \@topsep\@topsepadd + \advance\@topsep\parskip + \addvspace\@topsep + \fi + \fi + %\showthe \@topsepadd + %\showthe \topsep + %\showthe \partopsep + %\showthe \parskip + \global\@nobreakfalse + \global\@inlabelfalse + \global\@minipagefalse + \global\@newlistfalse} + +\def\FV@EndList{% + \FV@ListProcessLastLine + \FV@EndListFrame + %\showthe \@topsepadd + \@endparenv + \endgroup + \@endpetrue} + +\def\theFancyVerbLine{\sffamily\scriptsize\arabic{FancyVerbLine}} diff --git a/leetcode/Java/README.md b/leetcode/Java/README.md new file mode 100644 index 00000000..8886a0b4 --- /dev/null +++ b/leetcode/Java/README.md @@ -0,0 +1,3 @@ +#Java版 +----------------- +书的内容与C++版一摸一样,不过代码是用Java写的。本书的代码要求 Java 6 以上。 diff --git a/leetcode/LICENSE b/leetcode/LICENSE new file mode 100644 index 00000000..cf29599a --- /dev/null +++ b/leetcode/LICENSE @@ -0,0 +1,12 @@ +Copyright (c) 2013, soulmachine and The Contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + * Neither the name of the soulmachine nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/leetcode/README.md b/leetcode/README.md new file mode 100644 index 00000000..fd9ff34a --- /dev/null +++ b/leetcode/README.md @@ -0,0 +1,75 @@ +#LeetCode题解 +----------------- +##PDF下载 +LeetCode题解(C++版).pdf + +C++ 文件夹下是C++版,内容一摸一样,代码是用C++写的, + +Java 文件夹下是Java版,内容一摸一样,代码是用Java写的 + +##LaTeX模板 +本书使用的是陈硕开源的[模板](https://github.com/chenshuo/typeset)。这个模板制作精良,很有taste,感谢陈硕 :) + +##在Windows下编译 +1. 安装Tex Live 2013 。把bin目录例如`D:\texlive\2013\bin\win32`加入PATH环境变量。 +1. 安装字体。这个LaTex模板总共使用了9个字体,下载地址 ,有的字体Windows自带了,有的字体Ubuntu自带了,但都不全,还是一次性安装完所有字体比较方便。 +1. 安装TeXstudio +1. (可选)启动Tex Live Manager,更新所有已安装的软件包。 +1. 配置TeXstudio。 + + 启动Texstudio,选择 `Options-->Configure Texstudio-->Commands`,XeLaTex 设置为 `xelatex -synctex=1 -interaction=nonstopmode %.tex`; + + 选择 `Options-->Configure Texstudio-->Build` + + Build & View 由默认的 PDF Chain 改为 Compile & View; + + Default Compiler 由默认的PdfLaTex 修改为 XeLaTex ; + + PDF Viewer 改为 “Internal PDF Viewer(windowed)”,这样预览时会弹出一个独立的窗口,这样比较方便。 + +1. 编译。用TeXstudio打开`leetcode-cpp.tex`,点击界面上的绿色箭头就可以开始编译了。 + + 在下方的窗口可以看到TeXstudio正在使用的编译命令是`xelatex -synctex=1 -interaction=nonstopmode "leetcode-cpp".tex` + +##在Ubuntu下编译 +1. 安装Tex Live 2013 + + 1.1. 下载TexLive 2013 的ISO 光盘,地址 + + 1.2 mount 光盘,`sudo ./install-tl` 开始安装 + + 1.3 加入环境变量 + + sudo vi /etc/profile + export PATH=$PATH:/usr/local/texlive/2013/bin/x86_64-linux + export MANPATH=$MANPATH:/usr/local/texlive/2013/texmf-dist/doc/man + export INFPATH=$INFPATH:/usr/local/texlive/2013/texmf-dist/doc/info + +1. 安装字体。这个LaTex模板总共使用了9个字体,下载地址 ,有的字体Windows自带了,有的字体Ubuntu自带了,但都不全,还是一次性安装完所有字体比较方便。 +1. 安装TeXstudio +1. 配置TeXstudio。 + + 启动Texstudio,选择 `Options-->Configure Texstudio-->Commands`,XeLaTex 设置为 `xelatex -synctex=1 -interaction=nonstopmode %.tex`; + + 选择 `Options-->Configure Texstudio-->Build` + + Build & View 由默认的 PDF Chain 改为 Compile & View; + + Default Compiler 由默认的PdfLaTex 修改为 XeLaTex ; + + PDF Viewer 改为 “Internal PDF Viewer(windowed)”,这样预览时会弹出一个独立的窗口,这样比较方便。 + +1. 编译。用TeXstudio打开`leetcode-cpp.tex`,点击界面上的绿色箭头就可以开始编译了。 + + 在下方的窗口可以看到TeXstudio正在使用的编译命令是`xelatex -synctex=1 -interaction=nonstopmode "leetcode-cpp".tex` +1. 懒人版镜像。如果不想进行上面繁琐的安装过程,我做好了一个Ubuntu VMware虚拟机镜像,已经装好了TexLive 2013, TexStudio和字体(详细的安装日志见压缩包注释),开箱即用,下载地址 。 + +##如何贡献代码 +编译通过后,就具备了完整的LaTeX编译环境了。 + +本书模板已经写好了,基本上不需要很多LaTeX知识就可以动手了。 + +欢迎给本书添加内容或纠正错误,在自己本地编译成PDF,预览没问题后,就可以发pull request过来了。 + +##北美求职微博群 +我和我的小伙伴们在这里: diff --git "a/leetcode/\345\217\202\350\200\203\350\265\204\346\226\231/Longest Palindromic Substring Part II - LeetCode.com.pdf" "b/leetcode/\345\217\202\350\200\203\350\265\204\346\226\231/Longest Palindromic Substring Part II - LeetCode.com.pdf" new file mode 100644 index 00000000..e569ad42 Binary files /dev/null and "b/leetcode/\345\217\202\350\200\203\350\265\204\346\226\231/Longest Palindromic Substring Part II - LeetCode.com.pdf" differ diff --git "a/leetcode/\345\217\202\350\200\203\350\265\204\346\226\231/Manacher's ALGORITHM_ O(n)\346\227\266\351\227\264\346\261\202\345\255\227\347\254\246\344\270\262\347\232\204\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\344\270\262 - Blog of Felix021 - TechOnly.pdf" "b/leetcode/\345\217\202\350\200\203\350\265\204\346\226\231/Manacher's ALGORITHM_ O(n)\346\227\266\351\227\264\346\261\202\345\255\227\347\254\246\344\270\262\347\232\204\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\344\270\262 - Blog of Felix021 - TechOnly.pdf" new file mode 100644 index 00000000..5b614368 Binary files /dev/null and "b/leetcode/\345\217\202\350\200\203\350\265\204\346\226\231/Manacher's ALGORITHM_ O(n)\346\227\266\351\227\264\346\261\202\345\255\227\347\254\246\344\270\262\347\232\204\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\344\270\262 - Blog of Felix021 - TechOnly.pdf" differ diff --git "a/leetcode/\345\217\202\350\200\203\350\265\204\346\226\231/leetcode Single Number II - \344\275\215\350\277\220\347\256\227\345\244\204\347\220\206\346\225\260\347\273\204\344\270\255\347\232\204\346\225\260 - \344\273\243\351\207\221\346\241\245 - \345\215\232\345\256\242\345\233\255.pdf" "b/leetcode/\345\217\202\350\200\203\350\265\204\346\226\231/leetcode Single Number II - \344\275\215\350\277\220\347\256\227\345\244\204\347\220\206\346\225\260\347\273\204\344\270\255\347\232\204\346\225\260 - \344\273\243\351\207\221\346\241\245 - \345\215\232\345\256\242\345\233\255.pdf" new file mode 100644 index 00000000..4e528219 Binary files /dev/null and "b/leetcode/\345\217\202\350\200\203\350\265\204\346\226\231/leetcode Single Number II - \344\275\215\350\277\220\347\256\227\345\244\204\347\220\206\346\225\260\347\273\204\344\270\255\347\232\204\346\225\260 - \344\273\243\351\207\221\346\241\245 - \345\215\232\345\256\242\345\233\255.pdf" differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.sdf b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.sdf new file mode 100644 index 00000000..2ccf89d0 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.sdf differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.sln b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.sln new file mode 100644 index 00000000..aa78e197 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AP3_3_3_BalanceSign", "AP3_3_3_BalanceSign\AP3_3_3_BalanceSign.vcxproj", "{74890694-6D73-4FFA-9D48-5CC54E04E961}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {74890694-6D73-4FFA-9D48-5CC54E04E961}.Debug|Win32.ActiveCfg = Debug|Win32 + {74890694-6D73-4FFA-9D48-5CC54E04E961}.Debug|Win32.Build.0 = Debug|Win32 + {74890694-6D73-4FFA-9D48-5CC54E04E961}.Release|Win32.ActiveCfg = Release|Win32 + {74890694-6D73-4FFA-9D48-5CC54E04E961}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.suo b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.suo new file mode 100644 index 00000000..7437460c Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.suo differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.aps b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.aps new file mode 100644 index 00000000..4fb6c69e Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.aps differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.rc b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.rc new file mode 100644 index 00000000..fcdf55b4 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.rc differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.vcxproj b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.vcxproj new file mode 100644 index 00000000..cba9942e --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.vcxproj @@ -0,0 +1,90 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {74890694-6D73-4FFA-9D48-5CC54E04E961} + Win32Proj + AP3_3_3_BalanceSign + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.vcxproj.filters b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.vcxproj.filters new file mode 100644 index 00000000..1d7c7add --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.vcxproj.filters @@ -0,0 +1,38 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 源文件 + + + 源文件 + + + + + 头文件 + + + 头文件 + + + + + 资源文件 + + + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.vcxproj.user b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.vcxproj.user new file mode 100644 index 00000000..ace9a86a --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/BalanceSign.c b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/BalanceSign.c new file mode 100644 index 00000000..f0d03a16 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/BalanceSign.c @@ -0,0 +1,64 @@ +#include +#include +#include "Stack.h" +void main() +{ + char buf[50]; + Stack S; + unsigned int i; + char temp; +// buf = getline(); +// scanf_s("%s", buf); + gets(buf); + S = CreateStack(); + for( i=0; i + + + + + + + + + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.exe.embed.manifest.res b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.exe.embed.manifest.res new file mode 100644 index 00000000..9c8df0e3 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.exe.embed.manifest.res differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.exe.intermediate.manifest b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.exe.intermediate.manifest new file mode 100644 index 00000000..ecea6f7f --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.exe.intermediate.manifest @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.lastbuildstate b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.lastbuildstate new file mode 100644 index 00000000..59dd1ed9 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.lastbuildstate @@ -0,0 +1,2 @@ +#v4.0:v100:false +Debug|Win32|F:\Programming Exercise\Data Structures and Algorithm Analysis in C\AP3_3_3_BalanceSign\| diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.log b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.log new file mode 100644 index 00000000..b954ccff --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.log @@ -0,0 +1,32 @@ +生成启动时间为 2014/12/22 17:04:25。 + 1>项目“F:\Programming Exercise\Data Structures and Algorithm Analysis in C\AP3_3_3_BalanceSign\AP3_3_3_BalanceSign\AP3_3_3_BalanceSign.vcxproj”在节点 2 上(build 个目标)。 + 1>InitializeBuildStatus: + 正在创建“Debug\AP3_3_3_BalanceSign.unsuccessfulbuild”,因为已指定“AlwaysCreate”。 + ClCompile: + d:\Visual Studio 2010\VC\bin\CL.exe /c /ZI /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _DEBUG /D _CONSOLE /D _UNICODE /D UNICODE /Gm /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Fo"Debug\\" /Fd"Debug\vc100.pdb" /Gd /TC /analyze- /errorReport:prompt BalanceSign.c + BalanceSign.c + 1>f:\programming exercise\data structures and algorithm analysis in c\ap3_3_3_balancesign\ap3_3_3_balancesign\balancesign.c(12): warning C4996: 'gets': This function or variable may be unsafe. Consider using gets_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. + d:\visual studio 2010\vc\include\stdio.h(277) : 参见“gets”的声明 + ResourceCompile: + 所有输出均为最新。 + ManifestResourceCompile: + 所有输出均为最新。 + Link: + d:\Visual Studio 2010\VC\bin\link.exe /ERRORREPORT:PROMPT /OUT:"F:\Programming Exercise\Data Structures and Algorithm Analysis in C\AP3_3_3_BalanceSign\Debug\AP3_3_3_BalanceSign.exe" /INCREMENTAL /NOLOGO kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /MANIFEST /ManifestFile:"Debug\AP3_3_3_BalanceSign.exe.intermediate.manifest" /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /PDB:"F:\Programming Exercise\Data Structures and Algorithm Analysis in C\AP3_3_3_BalanceSign\Debug\AP3_3_3_BalanceSign.pdb" /SUBSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE /NXCOMPAT /IMPLIB:"F:\Programming Exercise\Data Structures and Algorithm Analysis in C\AP3_3_3_BalanceSign\Debug\AP3_3_3_BalanceSign.lib" /MACHINE:X86 Debug\AP3_3_3_BalanceSign.res + Debug\AP3_3_3_BalanceSign.exe.embed.manifest.res + Debug\BalanceSign.obj + Debug\stack.obj + Manifest: + C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\mt.exe /nologo /verbose /out:"Debug\AP3_3_3_BalanceSign.exe.embed.manifest" /manifest Debug\AP3_3_3_BalanceSign.exe.intermediate.manifest + 所有输出均为最新。 + LinkEmbedManifest: + 所有输出均为最新。 + AP3_3_3_BalanceSign.vcxproj -> F:\Programming Exercise\Data Structures and Algorithm Analysis in C\AP3_3_3_BalanceSign\Debug\AP3_3_3_BalanceSign.exe + FinalizeBuildStatus: + 正在删除文件“Debug\AP3_3_3_BalanceSign.unsuccessfulbuild”。 + 正在对“Debug\AP3_3_3_BalanceSign.lastbuildstate”执行 Touch 任务。 + 1>已完成生成项目“F:\Programming Exercise\Data Structures and Algorithm Analysis in C\AP3_3_3_BalanceSign\AP3_3_3_BalanceSign\AP3_3_3_BalanceSign.vcxproj”(build 个目标)的操作。 + +生成成功。 + +已用时间 00:00:00.90 diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.res b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.res new file mode 100644 index 00000000..36f26e23 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.res differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.vcxprojResolveAssemblyReference.cache b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.vcxprojResolveAssemblyReference.cache new file mode 100644 index 00000000..6da2f5ab Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.vcxprojResolveAssemblyReference.cache differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.write.1.tlog new file mode 100644 index 00000000..e69de29b diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign_manifest.rc b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign_manifest.rc new file mode 100644 index 00000000..8f884db3 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign_manifest.rc differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/BalanceSign.obj b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/BalanceSign.obj new file mode 100644 index 00000000..a29ce39f Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/BalanceSign.obj differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/CL.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/CL.read.1.tlog new file mode 100644 index 00000000..acb119bd Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/CL.read.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/CL.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/CL.write.1.tlog new file mode 100644 index 00000000..7a49009d Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/CL.write.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/cl.command.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/cl.command.1.tlog new file mode 100644 index 00000000..a0dfa78c Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/cl.command.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link-cvtres.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link-cvtres.read.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link-cvtres.read.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link-cvtres.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link-cvtres.write.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link-cvtres.write.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.4624-cvtres.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.4624-cvtres.read.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.4624-cvtres.read.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.4624-cvtres.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.4624-cvtres.write.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.4624-cvtres.write.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.4624.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.4624.read.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.4624.read.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.4624.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.4624.write.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.4624.write.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.5316-cvtres.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.5316-cvtres.read.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.5316-cvtres.read.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.5316-cvtres.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.5316-cvtres.write.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.5316-cvtres.write.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.5316.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.5316.read.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.5316.read.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.5316.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.5316.write.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.5316.write.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.command.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.command.1.tlog new file mode 100644 index 00000000..32fe67d1 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.command.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.read.1.tlog new file mode 100644 index 00000000..690998bf Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.read.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.write.1.tlog new file mode 100644 index 00000000..1b035c3e Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/link.write.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/mt.command.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/mt.command.1.tlog new file mode 100644 index 00000000..362fe22e Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/mt.command.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/mt.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/mt.read.1.tlog new file mode 100644 index 00000000..3cdff57d Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/mt.read.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/mt.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/mt.write.1.tlog new file mode 100644 index 00000000..3cdff57d Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/mt.write.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/rc.command.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/rc.command.1.tlog new file mode 100644 index 00000000..9df7f479 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/rc.command.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/rc.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/rc.read.1.tlog new file mode 100644 index 00000000..871de989 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/rc.read.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/rc.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/rc.write.1.tlog new file mode 100644 index 00000000..c717e425 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/rc.write.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/stack.obj b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/stack.obj new file mode 100644 index 00000000..434141f2 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/stack.obj differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/vc100.idb b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/vc100.idb new file mode 100644 index 00000000..a8cf9686 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/vc100.idb differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/vc100.pdb b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/vc100.pdb new file mode 100644 index 00000000..c6967302 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Debug/vc100.pdb differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Stack.cpp b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Stack.cpp new file mode 100644 index 00000000..41dfd059 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Stack.cpp @@ -0,0 +1,3 @@ + +#include "Stack.h" + diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Stack.h b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Stack.h new file mode 100644 index 00000000..9e5a0eab --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/Stack.h @@ -0,0 +1,15 @@ +#ifndef _STACK_H_ +#define _STACK_H_ + +struct Node; +typedef struct Node *PtrToNode; +typedef PtrToNode Stack; + +int IsEmpty(Stack S); +Stack CreateStack(void); +void MakeEmpty(Stack S); +void Pop(Stack S); +void Push(char x, Stack S); +char Top(Stack S); +#endif + diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/resource.h b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/resource.h new file mode 100644 index 00000000..8d4df29f --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by AP3_3_3_BalanceSign.rc + +// ¶һĬֵ +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/stack.c b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/stack.c new file mode 100644 index 00000000..086ababc --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/AP3_3_3_BalanceSign/stack.c @@ -0,0 +1,81 @@ +#include "stdlib.h" +#include "stdio.h" +#include "Stack.h" + +struct Node +{ + char Element; + PtrToNode Next; +}; + +int IsEmpty(Stack S) +{ + return S->Next == NULL; +} +Stack CreateStack(void) +{ + Stack S; + S = malloc(sizeof(struct Node)); + if(S == NULL) + { + printf_s("Out of space"); + } + S->Next = NULL; + return S; +} +void MakeEmpty(Stack S) +{ + if(S == NULL) + { + printf_s("must use Createstack first"); + } + else + { + while(!IsEmpty(S)) + { + Pop(S); + } + } +} +void Pop(Stack S) +{ + PtrToNode FirstCell; + if(IsEmpty(S)) + { + printf_s("Empty stack"); + } + else + { + FirstCell = S->Next; + S->Next = S->Next->Next; + free(FirstCell); + } +} +void Push(char x, Stack S) +{ + PtrToNode TmpCell; + TmpCell = malloc(sizeof(struct Node)); + if(TmpCell == NULL) + { + printf_s("out of space"); + } + else + { + TmpCell->Element = x; + TmpCell->Next = S->Next; + S->Next = TmpCell; + } +} + +char Top(Stack S) +{ + if(S->Next == NULL) + { + printf_s("stack is empty"); + return 0; + } + else + { + return S->Next->Element; + } +} \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.exe b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.exe new file mode 100644 index 00000000..9b16a049 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.exe differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.ilk b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.ilk new file mode 100644 index 00000000..c09ee1f6 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.ilk differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.pdb b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.pdb new file mode 100644 index 00000000..c8e0b83a Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/Debug/AP3_3_3_BalanceSign.pdb differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/ipch/ap3_3_3_balancesign-1b80f0b1/ap3_3_3_balancesign-8e6210aa.ipch b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/ipch/ap3_3_3_balancesign-1b80f0b1/ap3_3_3_balancesign-8e6210aa.ipch new file mode 100644 index 00000000..9af23896 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/AP3_3_3_BalanceSign/ipch/ap3_3_3_balancesign-1b80f0b1/ap3_3_3_balancesign-8e6210aa.ipch differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/Debug/List_Sample.exe b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/Debug/List_Sample.exe new file mode 100644 index 00000000..e5c38e4e Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/Debug/List_Sample.exe differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/Debug/List_Sample.ilk b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/Debug/List_Sample.ilk new file mode 100644 index 00000000..25f49f4f Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/Debug/List_Sample.ilk differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/Debug/List_Sample.pdb b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/Debug/List_Sample.pdb new file mode 100644 index 00000000..43ec8703 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/Debug/List_Sample.pdb differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample.sdf b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample.sdf new file mode 100644 index 00000000..2a5ea486 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample.sdf differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample.sln b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample.sln new file mode 100644 index 00000000..5d5959bf --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "List_Sample", "List_Sample\List_Sample.vcxproj", "{36BC1E94-4EB8-485B-B179-F41FF76C2552}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {36BC1E94-4EB8-485B-B179-F41FF76C2552}.Debug|Win32.ActiveCfg = Debug|Win32 + {36BC1E94-4EB8-485B-B179-F41FF76C2552}.Debug|Win32.Build.0 = Debug|Win32 + {36BC1E94-4EB8-485B-B179-F41FF76C2552}.Release|Win32.ActiveCfg = Release|Win32 + {36BC1E94-4EB8-485B-B179-F41FF76C2552}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample.suo b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample.suo new file mode 100644 index 00000000..9a604ce8 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample.suo differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/CL.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/CL.read.1.tlog new file mode 100644 index 00000000..3955b443 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/CL.read.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/CL.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/CL.write.1.tlog new file mode 100644 index 00000000..0a26e324 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/CL.write.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.exe.embed.manifest b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.exe.embed.manifest new file mode 100644 index 00000000..11bb704c --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.exe.embed.manifest @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.exe.embed.manifest.res b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.exe.embed.manifest.res new file mode 100644 index 00000000..9c8df0e3 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.exe.embed.manifest.res differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.exe.intermediate.manifest b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.exe.intermediate.manifest new file mode 100644 index 00000000..ecea6f7f --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.exe.intermediate.manifest @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.lastbuildstate b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.lastbuildstate new file mode 100644 index 00000000..7aab1bd6 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.lastbuildstate @@ -0,0 +1,2 @@ +#v4.0:v100:false +Debug|Win32|F:\Git Repository\xiangyu\Data Structures and Algorithm Analysis in C\List_Sample\| diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.log b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.log new file mode 100644 index 00000000..79620c80 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.log @@ -0,0 +1,37 @@ +生成启动时间为 2015/1/28 11:16:03。 + 1>项目“F:\Git Repository\xiangyu\Data Structures and Algorithm Analysis in C\List_Sample\List_Sample\List_Sample.vcxproj”在节点 2 上(build 个目标)。 + 1>InitializeBuildStatus: + 正在创建“Debug\List_Sample.unsuccessfulbuild”,因为已指定“AlwaysCreate”。 + ClCompile: + d:\Visual Studio 2010\VC\bin\CL.exe /c /ZI /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _DEBUG /D _CONSOLE /D _UNICODE /D UNICODE /Gm /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Fo"Debug\\" /Fd"Debug\vc100.pdb" /Gd /TC /analyze- /errorReport:prompt List_Sample.c Main.c + Main.c + List_Sample.c + 1>f:\git repository\xiangyu\data structures and algorithm analysis in c\list_sample\list_sample\list_sample.c(405): warning C4996: 'scanf': This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. + d:\visual studio 2010\vc\include\stdio.h(304) : 参见“scanf”的声明 + 1>f:\git repository\xiangyu\data structures and algorithm analysis in c\list_sample\list_sample\list_sample.c(417): warning C4101: “LPos”: 未引用的局部变量 + 1>f:\git repository\xiangyu\data structures and algorithm analysis in c\list_sample\list_sample\list_sample.c(418): warning C4101: “i”: 未引用的局部变量 + 1>f:\git repository\xiangyu\data structures and algorithm analysis in c\list_sample\list_sample\list_sample.c(417): warning C4101: “ResultPos”: 未引用的局部变量 + 1>f:\git repository\xiangyu\data structures and algorithm analysis in c\list_sample\list_sample\list_sample.c(418): warning C4101: “j”: 未引用的局部变量 + 正在生成代码... + 1>f:\git repository\xiangyu\data structures and algorithm analysis in c\list_sample\list_sample\list_sample.c(421): warning C4716: “PolynominalExp”: 必须返回一个值 + ManifestResourceCompile: + 所有输出均为最新。 + Link: + d:\Visual Studio 2010\VC\bin\link.exe /ERRORREPORT:PROMPT /OUT:"F:\Git Repository\xiangyu\Data Structures and Algorithm Analysis in C\List_Sample\Debug\List_Sample.exe" /INCREMENTAL /NOLOGO opencv_ml249d.lib opencv_calib3d249d.lib opencv_contrib249d.lib opencv_core249d.lib opencv_features2d249d.lib opencv_flann249d.lib opencv_gpu249d.lib opencv_highgui249d.lib opencv_imgproc249d.lib opencv_legacy249d.lib opencv_objdetect249d.lib opencv_ts249d.lib opencv_video249d.lib opencv_nonfree249d.lib opencv_ocl249d.lib opencv_photo249d.lib opencv_stitching249d.lib opencv_superres249d.lib opencv_videostab249d.lib opencv_objdetect249.lib opencv_ts249.lib opencv_video249.lib opencv_nonfree249.lib opencv_ocl249.lib opencv_photo249.lib opencv_stitching249.lib opencv_superres249.lib opencv_videostab249.lib opencv_calib3d249.lib opencv_contrib249.lib opencv_core249.lib opencv_features2d249.lib opencv_flann249.lib opencv_gpu249.lib opencv_highgui249.lib opencv_imgproc249.lib opencv_legacy249.lib opencv_ml249.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /MANIFEST /ManifestFile:"Debug\List_Sample.exe.intermediate.manifest" /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /PDB:"F:\Git Repository\xiangyu\Data Structures and Algorithm Analysis in C\List_Sample\Debug\List_Sample.pdb" /SUBSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE /NXCOMPAT /IMPLIB:"F:\Git Repository\xiangyu\Data Structures and Algorithm Analysis in C\List_Sample\Debug\List_Sample.lib" /MACHINE:X86 Debug\List_Sample.exe.embed.manifest.res + Debug\List_Sample.obj + Debug\Main.obj + Debug\stack.obj + Manifest: + C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\mt.exe /nologo /verbose /out:"Debug\List_Sample.exe.embed.manifest" /manifest Debug\List_Sample.exe.intermediate.manifest + 所有输出均为最新。 + LinkEmbedManifest: + 所有输出均为最新。 + List_Sample.vcxproj -> F:\Git Repository\xiangyu\Data Structures and Algorithm Analysis in C\List_Sample\Debug\List_Sample.exe + FinalizeBuildStatus: + 正在删除文件“Debug\List_Sample.unsuccessfulbuild”。 + 正在对“Debug\List_Sample.lastbuildstate”执行 Touch 任务。 + 1>已完成生成项目“F:\Git Repository\xiangyu\Data Structures and Algorithm Analysis in C\List_Sample\List_Sample\List_Sample.vcxproj”(build 个目标)的操作。 + +生成成功。 + +已用时间 00:00:01.70 diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.obj b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.obj new file mode 100644 index 00000000..2a0c1193 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.obj differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.vcxprojResolveAssemblyReference.cache b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.vcxprojResolveAssemblyReference.cache new file mode 100644 index 00000000..6da2f5ab Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.vcxprojResolveAssemblyReference.cache differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample.write.1.tlog new file mode 100644 index 00000000..e69de29b diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample_manifest.rc b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample_manifest.rc new file mode 100644 index 00000000..9f7a1303 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/List_Sample_manifest.rc differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/Main.obj b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/Main.obj new file mode 100644 index 00000000..825b1dc4 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/Main.obj differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/cl.command.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/cl.command.1.tlog new file mode 100644 index 00000000..ab5b412d Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/cl.command.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link-cvtres.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link-cvtres.read.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link-cvtres.read.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link-cvtres.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link-cvtres.write.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link-cvtres.write.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.5900-cvtres.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.5900-cvtres.read.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.5900-cvtres.read.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.5900-cvtres.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.5900-cvtres.write.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.5900-cvtres.write.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.5900.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.5900.read.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.5900.read.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.5900.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.5900.write.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.5900.write.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.7128-cvtres.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.7128-cvtres.read.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.7128-cvtres.read.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.7128-cvtres.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.7128-cvtres.write.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.7128-cvtres.write.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.7128.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.7128.read.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.7128.read.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.7128.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.7128.write.1.tlog new file mode 100644 index 00000000..46b134b1 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.7128.write.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.command.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.command.1.tlog new file mode 100644 index 00000000..e2680875 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.command.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.read.1.tlog new file mode 100644 index 00000000..19d3c265 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.read.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.write.1.tlog new file mode 100644 index 00000000..084b47c1 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/link.write.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/mt.command.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/mt.command.1.tlog new file mode 100644 index 00000000..11619b10 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/mt.command.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/mt.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/mt.read.1.tlog new file mode 100644 index 00000000..ffebc7df Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/mt.read.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/mt.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/mt.write.1.tlog new file mode 100644 index 00000000..ffebc7df Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/mt.write.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/rc.command.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/rc.command.1.tlog new file mode 100644 index 00000000..e1dd57e0 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/rc.command.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/rc.read.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/rc.read.1.tlog new file mode 100644 index 00000000..0e094936 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/rc.read.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/rc.write.1.tlog b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/rc.write.1.tlog new file mode 100644 index 00000000..fe396e55 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/rc.write.1.tlog differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/stack.obj b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/stack.obj new file mode 100644 index 00000000..87b749c7 Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/stack.obj differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/vc100.idb b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/vc100.idb new file mode 100644 index 00000000..5c6f64de Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/vc100.idb differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/vc100.pdb b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/vc100.pdb new file mode 100644 index 00000000..2cff627d Binary files /dev/null and b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Debug/vc100.pdb differ diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/List_Sample.c b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/List_Sample.c new file mode 100644 index 00000000..75ce32af --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/List_Sample.c @@ -0,0 +1,421 @@ + +#include +#include +#include +#include "List_Sample.h" +struct Node +{ + ElementType Element; + Position Next; +}; +/********************************** +Describe: + һҪԪظԪֵ +************************************/ +List CreateList() +{ + ElementType data = 0; + int num = 0; + List head; + List r,s; + int i; + head = (List)malloc(sizeof(struct Node)); + head->Element = 0; + r = head; + printf_s("enter the number of list's elements: "); + scanf_s("%d", &num); +// printf_s("%d" , num); + for(i = 1; i<=num; i++) + { + printf_s("enter data[%d]: ", i); + scanf_s("%d", &data); + s = (List)malloc(sizeof(struct Node)); + if(s == NULL) + { + printf_s("out of space"); + } + s->Element = data; + r->Next = s; + + r = s; + } + s->Next = NULL; + printf_s("the List is done\n"); + return head; +} +List InitEmpty() +{ + List L; + L = malloc(sizeof(struct Node)); + L->Element = 0; + L->Next = NULL; + return L; +} +List MakeEmpty(List L) +{ + List temp, temp1; + temp = L->Next; + while(temp!=NULL) + { + temp1 = temp; + temp = temp->Next; + free(temp1); + } + L->Next = NULL; + return L; +} +/***************************************** +Describe: + жһǷΪ 1ʾΪ +******************************************/ +int IsEmpty(List L) +{ + return L->Next == NULL; +} +/****************************************** +Describe: + жλnԪǷΪһԪ +*******************************************/ +int IsLast(Position n, List L) +{ + return n->Next == NULL; +} +/****************************************** +Describtion: + LвҺԪxĵԪش˵Ԫָ +********************************************/ +Position Find(ElementType x, List L) +{ + List temp; + temp = L->Next; + while(temp->Next != NULL) + { + if(temp->Element == x) + { + return temp; + } + temp = temp->Next; + } + printf_s("no element you want"); + return 0; +} +/************************************************* +Describtion: + LвԪxذxԪصĵԪǰһԪָ +***************************************************/ +Position FindPrevious(ElementType x, List L) +{ + List temp, pretemp; + pretemp = L; + temp = L->Next; + if(temp == NULL) + { + printf_s("this is an empty list"); + return L; + } + while(temp->Next != NULL) + { + if(temp->Element == x) + { + return pretemp; + } + pretemp = temp; + temp = temp->Next; + } + printf_s("no element you want"); + return 0; +} +/******************************************* +Describtion: + LɾԪx +*********************************************/ +void Delete(ElementType x, List L) +{ + List dest, predest; +// dest = Find(x, L); + predest = FindPrevious(x, L); + dest = predest->Next; + predest->Next = dest->Next; + free(dest); +} +/*********************************************** +insert the x after the Position P in List L +***********************************************/ +void Insert(ElementType x, List L, Position p) +{ + List temp; + temp = (List)malloc(sizeof(struct Node)); + if(temp == NULL) + { + printf_s("fatal error out of space"); + return; + } + temp->Element = x; + temp->Next = p->Next; + p->Next = temp; +} +/****************************************** +Describtion: + ɾLעҪͷڴ +*******************************************/ +void DeleteList(List L) +{ + List temp, P; + P = L->Next; + while(P != NULL) + { + temp = P->Next; + free(P); + P = temp; + } +} +/*************************************** +Describtion: + ӡLеԪֵ +****************************************/ +void PrintList(List L) +{ + List temp; + if(L->Next == NULL) + { + printf_s("List is Empty"); + } + else + { + temp = L->Next; + while(temp != NULL) + { + printf_s(" %d ", temp->Element); + temp = temp->Next; + } + printf("\n"); + } +} +/************************************************* +Describetion: + ӡLеĵnԪԪ +***************************************************/ +void PrintList_Element(List L, int n) +{ + int i; + List temp; + temp = L; + for(i=1; i<=n; i++) + { + temp = temp->Next; + if(temp == NULL) + { + printf(" %d is out of the length of List ", n); + return; + } + } + printf(" %d ", temp->Element); +} +/******************************************* +Describetion: + ˳Ӧݽṹ㷨ڶ棩ϰ3.3.2 + ӡLЩPָλϵԪء磬 + p = 1, 3, 4, 6,ô Lеĵ1 3 4 6Ԫ + ӡ +********************************************/ +void PrintLots(List L, List P) +{ + List Ptemp; + Ptemp = P->Next; + while(Ptemp != NULL) + { + PrintList_Element( L, Ptemp->Element ); + Ptemp = Ptemp->Next; + } + printf("\n"); +} +/************************************************* +Description: + ָͨеԪֵʹԪڵĵԪһԪ + нָͨʵ +*************************************************/ +void ExchangeElement_WithNext(ElementType x, List L) +{ + List temp1, temp2, temp3; + temp1 = FindPrevious(x, L); + temp2 = Find(x, L); + temp3 = temp2->Next; + temp1->Next = temp3; + temp2->Next = temp3->Next; + temp3->Next = temp2; +} +/**************************************************** +Description: + ָͨԪλãָ룩ʹһԪн + ָͨʵ +*****************************************************/ +void ExchangePosition_WithNext(Position n, List L) +{ + List temp1, temp2, temp3; + temp1 = FindPrevious(n->Element, L); + temp2 = n; + temp3 = temp2->Next; + temp1->Next = temp3; + temp2->Next = temp3->Next; + temp3->Next = temp2; +} +/************************************** +Description: + ˺ðݷʵԪذմС˳ + ע⣬ںÿɨҵСԪֵ֮ԪֵӦ + ԪԪؽл +******************************************/ +void Sort(List L) +{ + List min; + ElementType temp; + List P, Q; + Q = L->Next; + + while(Q != NULL) + { + P = Q; + min = P; + while(P != NULL) + { + if(min->Element > P->Element) + { + min = P; + } + P = P->Next; + } + temp = min->Element; + min->Element = Q->Element; + Q->Element = temp; + Q = Q->Next; + } +} + +Position First(List L) +{ + if(L->Next==NULL) + { + printf("List is empty!"); + } + return L->Next; +} +Position Header(List L) +{ + return L; +} +List Intersect(List L1, List L2) +{ + List Result; + Position L1Pos, L2Pos, ResultPos; + L1Pos = First(L1); + L2Pos = First(L2); + Result =InitEmpty(); + ResultPos = Result; + while(L1Pos!=NULL && L2Pos!=NULL) + { + if(L1Pos->Element > L2Pos->Element) + { + L2Pos = L2Pos->Next; + } + else if(L1Pos->Element < L2Pos->Element) + { + L1Pos = L1Pos->Next; + } + else + { + Insert(L1Pos->Element,Result, ResultPos); + ResultPos = ResultPos->Next; + L1Pos = L1Pos->Next; + L2Pos = L2Pos->Next; + } + } + return Result; +} +/************************************************* +Description: + Union()L1 L2ԪصIJ(ע⣺ԪرǾС) + ز + ע⣺е ResultPos = Result;ΪInsert()Posλõĺ룬Զһ + Ҫ뽫Ԫز뵽һλãPosӦΪhead. +**************************************************/ +List Union(List L1, List L2) +{ + List Result; + ElementType ResultElement; + List L1Pos, L2Pos, ResultPos; + Result = InitEmpty(); + L1Pos = L1->Next; + L2Pos = L2->Next; + ResultPos = Result; + while(L1Pos != NULL && L2Pos != NULL) + { + if(L1Pos->Element < L2Pos->Element) + { + ResultElement = L1Pos->Element; + L1Pos = L1Pos->Next; + } + else if(L2Pos->Element < L1Pos->Element) + { + ResultElement = L2Pos->Element; + L2Pos = L2Pos->Next; + } + else + { + ResultElement = L1Pos->Element; + L1Pos = L1Pos->Next; + L2Pos = L2Pos->Next; + + } + Insert(ResultElement, Result, ResultPos); + ResultPos = ResultPos->Next; + } + while(L1Pos != NULL) + { + Insert(L1Pos->Element, Result, ResultPos); + L1Pos = L1Pos->Next; + ResultPos = ResultPos->Next; + } + while(L2Pos != NULL) + { + Insert(L2Pos->Element, Result, ResultPos); + L2Pos = L2Pos->Next; + ResultPos = ResultPos->Next; + } + return Result; +} + +/************************************************ +Description: + nζʽմӵ͵ϵ + ϵ洢 +*************************************************/ +List Polynominal(int n) +{ + List L; + List LPos; + int a; + int i; + L = InitEmpty(); + LPos = L; + printf("%dζʽϵ:\n", n); + for(i=0; i<=n; i++) + { + + printf("%dϵ: ", i); + scanf("%d", &a); + Insert(a,L,LPos); + } + return L; +} +/***************************************** +Description: + δʵ +*****************************************/ +List PolynominalExp(List L, int n, int p) +{ + List Result; + List LPos, ResultPos; + int i, j; + Result = InitEmpty(); + +} \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/List_Sample.h b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/List_Sample.h new file mode 100644 index 00000000..1bef59dd --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/List_Sample.h @@ -0,0 +1,33 @@ +#ifndef _LIST_SAMPLE_H +#define _LIST_SAMPLE_H + +struct Node; +typedef int ElementType; +typedef struct Node *PtrToNode; +typedef PtrToNode List; +typedef PtrToNode Position; + +List CreateList(); +List InitEmpty(); +List MakeEmpty(List L); +int IsEmpty(List L); +int IsLast(Position n, List L); +Position Find(ElementType x, List L); +Position FindPrevious(ElementType x, List L); +Position First(List L); +Position Header(List L); +void Delete(ElementType x, List L); +void Insert(ElementType x, List L, Position p); +void DeleteList(List L); +void PrintList(List L); +void PrintList_Element(List L, int n); +void PrintLots(List L, List P); +void ExchangeElement_WithNext(ElementType x, List L); +void ExchangePosition_WithNext(Position x, List L); +void Sort(List L); +List Intersect(List L1, List L2); +List Union(List L1, List L2); +List Polynominal(int n); +List PolynominalExp(List L, int n, int p); + +#endif \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/List_Sample.vcxproj b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/List_Sample.vcxproj new file mode 100644 index 00000000..398d8a53 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/List_Sample.vcxproj @@ -0,0 +1,88 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {36BC1E94-4EB8-485B-B179-F41FF76C2552} + Win32Proj + List_Sample + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/List_Sample.vcxproj.filters b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/List_Sample.vcxproj.filters new file mode 100644 index 00000000..45140b98 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/List_Sample.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 源文件 + + + 源文件 + + + 源文件 + + + + + 头文件 + + + 头文件 + + + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/List_Sample.vcxproj.user b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/List_Sample.vcxproj.user new file mode 100644 index 00000000..ace9a86a --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/List_Sample.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Main.c b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Main.c new file mode 100644 index 00000000..d926822d --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Main.c @@ -0,0 +1,24 @@ +#include "List_Sample.h" +#include + +void main() +{ + List L; + + +// ElementType x; + L = Polynominal(3); + +// PrintList(L); +/* PrintList(L); + printf("enter the element you want to exchange\n"); + scanf("%d", &x); + P = Find(x, L); + ExchangePosition_WithNext(P, L); + */ +// Sort(L); + +// I = Union(L, P); + PrintList(L); +} + diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Stack.h b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Stack.h new file mode 100644 index 00000000..87b0960e --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/Stack.h @@ -0,0 +1,15 @@ +#ifndef _STACK_H_ +#define _STACK_H_ + +struct StackNode; +typedef struct StackNode *PtrToNode; +typedef PtrToNode Stack; + +int IsEmpty(Stack S); +Stack CreateStack(void); +void MakeEmpty(Stack S); +void Pop(Stack S); +void Push(char x, Stack S); +char Top(Stack S); +#endif + diff --git a/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/stack.c b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/stack.c new file mode 100644 index 00000000..9fcac9b6 --- /dev/null +++ b/xiangyu/Data Structures and Algorithm Analysis in C/List_Sample/List_Sample/stack.c @@ -0,0 +1,81 @@ +#include "stdlib.h" +#include "stdio.h" +#include "Stack.h" + +struct StackNode +{ + char Element; + PtrToNode Next; +}; + +int StackIsEmpty(Stack S) +{ + return S->Next == NULL; +} +Stack CreateStack(void) +{ + Stack S; + S = malloc(sizeof(struct StackNode)); + if(S == NULL) + { + printf_s("Out of space"); + } + S->Next = NULL; + return S; +} +void MakeEmptyStack(Stack S) +{ + if(S == NULL) + { + printf_s("must use Createstack first"); + } + else + { + while(!IsEmpty(S)) + { + Pop(S); + } + } +} +void Pop(Stack S) +{ + PtrToNode FirstCell; + if(IsEmpty(S)) + { + printf_s("Empty stack"); + } + else + { + FirstCell = S->Next; + S->Next = S->Next->Next; + free(FirstCell); + } +} +void Push(char x, Stack S) +{ + PtrToNode TmpCell; + TmpCell = malloc(sizeof(struct StackNode)); + if(TmpCell == NULL) + { + printf_s("out of space"); + } + else + { + TmpCell->Element = x; + TmpCell->Next = S->Next; + S->Next = TmpCell; + } +} + +char Top(Stack S) +{ + if(S->Next == NULL) + { + printf_s("stack is empty"); + return 0; + } + else + { + return S->Next->Element; + } +} \ No newline at end of file diff --git a/xiangyu/git_test b/xiangyu/git_test new file mode 100644 index 00000000..e69de29b diff --git a/xiangyu/xiangyu b/xiangyu/xiangyu new file mode 160000 index 00000000..3d0cc15c --- /dev/null +++ b/xiangyu/xiangyu @@ -0,0 +1 @@ +Subproject commit 3d0cc15caaa74fac153f60d848fa6427934a1caa