表单重复提交是在多用户Web应用中最常见、带来很多麻烦的一个问题。有很多的应用场景都会遇到重复提交问题,比如:
禁掉提交按钮。表单提交后使用Javascript使提交按钮disable。这种方法防止心急的用户多次点击按钮。但有个问题,如果客户端把Javascript给禁止掉,这种方法就无效了。
我之前的文章曾说过用一些Jquery插件效果不错。
Post/Redirect/Get模式。在提交后执行页面重定向,这就是所谓的Post-Redirect-Get (PRG)模式。简言之,当用户提交了表单后,你去执行一个客户端的重定向,转到提交成功信息页面。
这能避免用户按F5导致的重复提交,而其也不会出现浏览器表单重复提交的警告,也能消除按浏览器前进和后退按导致的同样问题。
在session中存放一个特殊标志。当表单页面被请求时,生成一个特殊的字符标志串,存在session中,同时放在表单的隐藏域里。接受处理表单数据时,检查标识字串是否存在,并立即从session中删除它,然后正常处理数据。
如果发现表单提交里没有有效的标志串,这说明表单已经被提交过了,忽略这次提交。
这使你的web应用有了更高级的XSRF保护。
在数据库里添加约束。在数据库里添加唯一约束或创建唯一索引,防止出现重复数据。这是最有效的防止重复提交数据的方法。
你是如何克服数据重复提交问题的?你遇到过什么重复提交数据的现实例子吗?
英文原文:Prevent Duplicate Form Submission
行文结构
二叉树遍历根据“根节点”遍历时相对的次序分为前序、中序、后序。图示为相对于根节点的次序,左右子树也是一样的规则。
前序遍历 中序遍历 后续遍历
1. 递归方法
前序遍历
void PreorderTraversal(BiTree T) { if(T) { cout << T->data << endl; PreorderTraversal(T->left); PreorderTraversal(T->right); } }
中序遍历
void InorderTraversal(BiTree T) { if(T) { InorderTraversal(T->left); cout << T->data << endl; InorderTraversal(T->right); } }
后续遍历
void PostorderTraversal(BiTree T) { if(T) { PostorderTraversal(T->left); PostorderTraversal(T->right); cout << T->data << endl; } }
整合参考程序
#include<iostream>
#include<vector>
using namespace std;
typedef struct BiTNode
{
int data;
BiTNode *left;
BiTNode *right;
}BiTNode, *BiTree;
bool SearchBST(BiTree T, int val, BiTree f, BiTree &p)
{
if(!T)
{
p = f;
return false;
}
else if(val == T->data)
{
p = T;
return true;
}
else if(val < T->data)
SearchBST(T->left, val, T, p);
else
SearchBST(T->right, val, T, p);
}
bool InsertBST(BiTree &T, int val)
{
BiTree p;
if(!SearchBST(T, val, NULL, p))
{
BiTree node = new BiTNode;
node->data = val;
node->left = node->right = NULL;
if(!p)
T = node;
else if(val < p->data)
p->left = node;
else
p->right = node;
return true;
}
return false;
}
void PreorderTraversal(BiTree T)
{
if(T)
{
cout << T->data << endl;
PreorderTraversal(T->left);
PreorderTraversal(T->right);
}
}
void InorderTraversal(BiTree T)
{
if(T)
{
InorderTraversal(T->left);
cout << T->data << endl;
InorderTraversal(T->right);
}
}
void PostorderTraversal(BiTree T)
{
if(T)
{
PostorderTraversal(T->left);
PostorderTraversal(T->right);
cout << T->data << endl;
}
}
int main()
{
int array[] = {5, 1, 0, 45, 10, 3, 8, 5};
int len_array = sizeof(array) / sizeof(*array);
BiTree root = NULL;
for(int i = 0; i< len_array; ++i)
InsertBST(root, array[i]);
cout << "PreorderTraversal" << endl;
PreorderTraversal(root);
cout << "----------------------" << endl;
cout << "InorderTraversal" << endl;
InorderTraversal(root);
cout << "----------------------" << endl;
cout << "PostorderTraversal" << endl;
PostorderTraversal(root);
cout << "----------------------" << endl;
}
结果
2. 非递归方法
非递归方法借助于栈,“记忆”走过的结点。
前序遍历
“根-左孩子-右孩子”,对每一个结点看成是根节点,先输出,后入栈(到时返回访问右孩子),然后访问左孩子。当不能在往左走时,查询栈中的记忆,根据栈顶进入右孩子,重复上边的故事。
void PreorderTraversal(BiTree T) { stack<BiTree> s; while(T || !s.empty()) { while(T) { cout << T->data << endl; s.push(T); T = T->left; } T = s.top(); T = T->right; s.pop(); } }
中序遍历
“左孩子-根-右孩子”,对每一个结点看成是根节点,先入栈(到时返回访问自己和右孩子),然后访问左孩子。当不能在往左走时,查询栈中的记忆,根据栈顶访问自己并且进入右孩子,重复上边的故事。
void InorderTraversal(BiTree T) { stack<BiTree> s; while(T || !s.empty()) { while(T) { s.push(T); T = T->left; } T = s.top(); cout << T->data << endl; T = T->right; s.pop(); } }
后续遍历
“左孩子-右孩子-根”,为了达到这个效果,需要把把按“根-->右孩子-->左孩子”的顺序入栈,因为栈的特征是先进后出,所以访问时顺序相反。对于一个结点只有满足以下两个条件时,才访问,否则按“根-->右孩子-->左孩子”顺序入栈。
void PostorderTraversal(BiTree T) { stack<BiTree> s; BiTree cur = NULL, pre = NULL; s.push(T); while(!s.empty()) { cur = s.top(); if((!cur->left && !cur->right) || (pre != NULL && (pre == cur->left || pre == cur->right))) { cout << cur->data << endl; pre = cur; s.pop(); } else { if(cur->right) s.push(cur->right); if(cur->left) s.push(cur->left); } } }
整合参考程序
#include<iostream>
#include<stack>
using namespace std;
typedef struct BiTNode
{
int data;
BiTNode *left;
BiTNode *right;
}BiTNode, *BiTree;
bool SearchBST(BiTree T, int val, BiTree f, BiTree &p)
{
if(!T)
{
p = f;
return false;
}
else if(val == T->data)
{
p = T;
return true;
}
else if(val < T->data)
SearchBST(T->left, val, T, p);
else
SearchBST(T->right, val, T, p);
}
bool InsertBST(BiTree &T, int val)
{
BiTree p;
if(!SearchBST(T, val, NULL, p))
{
BiTree node = new BiTNode;
node->data = val;
node->left = node->right = NULL;
if(!p)
T = node;
else if(val < p->data)
p->left = node;
else
p->right = node;
return true;
}
return false;
}
void PreorderTraversal(BiTree T)
{
stack<BiTree> s;
while(T || !s.empty())
{
while(T)
{
cout << T->data << endl;
s.push(T);
T = T->left;
}
T = s.top();
T = T->right;
s.pop();
}
}
void InorderTraversal(BiTree T)
{
stack<BiTree> s;
while(T || !s.empty())
{
while(T)
{
s.push(T);
T = T->left;
}
T = s.top();
cout << T->data << endl;
T = T->right;
s.pop();
}
}
void PostorderTraversal(BiTree T)
{
stack<BiTree> s;
BiTree cur = NULL, pre = NULL;
s.push(T);
while(!s.empty())
{
cur = s.top();
if((!cur->left && !cur->right) ||
(pre != NULL && (pre == cur->left || pre == cur->right)))
{
cout << cur->data << endl;
pre = cur;
s.pop();
}
else
{
if(cur->right)
s.push(cur->right);
if(cur->left)
s.push(cur->left);
}
}
}
int main()
{
int array[] = {5, 1, 0, 45, 10, 3, 8, 5};
int len_array = sizeof(array) / sizeof(*array);
BiTree root = NULL;
for(int i = 0; i< len_array; ++i)
InsertBST(root, array[i]);
cout << "PreorderTraversal" << endl;
PreorderTraversal(root);
cout << "----------------------" << endl;
cout << "InorderTraversal" << endl;
InorderTraversal(root);
cout << "----------------------" << endl;
cout << "PostorderTraversal" << endl;
PostorderTraversal(root);
cout << "----------------------" << endl;
}
结果同递归遍历
3. 微软面试题
分析:由二叉遍历树变成有序的结构,不用说绝对是中序遍历。中序遍历分为两种方法——递归、非递归。递归不需要额外的空间,非递归需要额外空间(借助与栈)。现在用两种方法实现,其思路都是基本中序遍历的改进。
方法一(递归方法)
void BST2BLink(BiTree T, BiTree &pre) //&地址操作,所有递归使用同一个 { if(T) { BST2BLink(T->left, pre); if(pre) { pre->right = T; T ->left = pre; } pre = T; BST2BLink(T->right, pre); } }
执行程序
#include<iostream>
#include<stack>
using namespace std;
typedef struct BiTNode
{
int data;
BiTNode *left;
BiTNode *right;
}BiTNode, *BiTree;
bool SearchBST(BiTree T, int val, BiTree f, BiTree &p)
{
if(!T)
{
p = f;
return false;
}
else if(val == T->data)
{
p = T;
return true;
}
else if(val < T->data)
SearchBST(T->left, val, T, p);
else
SearchBST(T->right, val, T, p);
}
bool InsertBST(BiTree &T, int val)
{
BiTree p;
if(!SearchBST(T, val, NULL, p))
{
BiTree node = new BiTNode;
node->data = val;
node->left = node->right = NULL;
if(!p)
T = node;
else if(val < p->data)
p->left = node;
else
p->right = node;
return true;
}
return false;
}
void BST2BLink(BiTree T, BiTree &pre)
{
if(T)
{
BST2BLink(T->left, pre);
if(pre)
{
pre->right = T;
T ->left = pre;
}
pre = T;
BST2BLink(T->right, pre);
}
}
int BST2BLink(BiTree &T)
{
stack<BiTree> s;
BiTree cur = T, pre = NULL;
while(cur || !s.empty())
{
while(cur)
{
s.push(cur);
cur = cur->left;
}
cur = s.top();
s.pop();
cout << cur->data << endl;
if(!pre)
T = cur;
else
{
pre->right = cur;
cur ->left = pre;
}
pre = cur;
cur = cur->right;
}
}
void BLinkTraversal(BiTree root)
{
while(root)
{
cout << root->data << endl;
root = root->right;
}
}
int main()
{
int array[] = {5, 8, 0, 3, 100, 45};
int len_array = sizeof(array) / sizeof(*array);
BiTree root = NULL;
for(int i = 0; i< len_array; ++i)
InsertBST(root, array[i]);
cout << "Recursion" << endl;
BiTree pre = NULL;
BST2BLink(root, pre);
while(root->left)
root = root->left;
BLinkTraversal(root);
/*
cout << "\nNot Recursion" << endl;
BST2BLink(root_copy);
cout << "----------------------" << endl;
BLinkTraversal(root);
*/
}
int BST2BLink(BiTree &T) { stack<BiTree> s; BiTree cur = T, pre = NULL; while(cur || !s.empty()) { while(cur) { s.push(cur); cur = cur->left; } cur = s.top(); s.pop(); cout << cur->data << endl; if(!pre) T = cur; else { pre->right = cur; cur ->left = pre; } pre = cur; cur = cur->right; } }
执行程序
#include<iostream>
#include<stack>
using namespace std;
typedef struct BiTNode
{
int data;
BiTNode *left;
BiTNode *right;
}BiTNode, *BiTree;
bool SearchBST(BiTree T, int val, BiTree f, BiTree &p)
{
if(!T)
{
p = f;
return false;
}
else if(val == T->data)
{
p = T;
return true;
}
else if(val < T->data)
SearchBST(T->left, val, T, p);
else
SearchBST(T->right, val, T, p);
}
bool InsertBST(BiTree &T, int val)
{
BiTree p;
if(!SearchBST(T, val, NULL, p))
{
BiTree node = new BiTNode;
node->data = val;
node->left = node->right = NULL;
if(!p)
T = node;
else if(val < p->data)
p->left = node;
else
p->right = node;
return true;
}
return false;
}
void BST2BLink(BiTree T, BiTree &pre)
{
if(T)
{
BST2BLink(T->left, pre);
if(pre)
{
pre->right = T;
T ->left = pre;
}
pre = T;
BST2BLink(T->right, pre);
}
}
int BST2BLink(BiTree &T)
{
stack<BiTree> s;
BiTree cur = T, pre = NULL;
while(cur || !s.empty())
{
while(cur)
{
s.push(cur);
cur = cur->left;
}
cur = s.top();
s.pop();
// cout << cur->data << endl;
if(!pre)
T = cur;
else
{
pre->right = cur;
cur ->left = pre;
}
pre = cur;
cur = cur->right;
}
}
void BLinkTraversal(BiTree root)
{
while(root)
{
cout << root->data << endl;
root = root->right;
}
}
int main()
{
int array[] = {5, 8, 0, 3, 100, 45};
int len_array = sizeof(array) / sizeof(*array);
BiTree root = NULL;
for(int i = 0; i< len_array; ++i)
InsertBST(root, array[i]);
/*
cout << "Recursion" << endl;
BiTree pre = NULL;
BST2BLink(root, pre);
while(root->left)
root = root->left;
BLinkTraversal(root);
*/
cout << "\nNot Recursion" << endl;
BST2BLink(root);
cout << "----------------------" << endl;
BLinkTraversal(root);
}